@程序员,React 使用如何避坑?

@程序员,如何更好地写React?

作者 | Alex K

译者 | 苏本如,责编 | 郭芮

出品 | CSDN(ID:CSDNnews)

以下为译文:

在Stack Overflow上回答与React框架相关的问题时,我注意到人们对于这个框架有几类主要的问题。我决定将一些最常见的问题和如何处理这些问题的解决方法写出来,以期对那些还不熟悉React框架的人,或那些正在努力掌握其基本概念的人有所帮助。在本文中,对于使用基于类组件和使用钩子(hook)的函数组件遇到的问题,都会交叉谈到。

直接修改状态

React中的状态被认为是不可变的,因此不应该直接修改。如果要修改状态值,应该使用一个特殊的setState方法和useState钩子中的setter函数。考虑下面的例子,在这个例子中,你希望根据复选框(checkbox)的状态更新数组中特定对象的checked字段。

 const updateFeaturesList = (e, idx) => {
listFeatures[idx].checked = e.target.checked;
setListFeatures(listFeatures);
};

这段代码的问题在于,对状态的更改不会反映到UI中,因为状态更新使用了相同的对象引用,因此不会触发重新渲染(re-render)动作。不能直接改变状态的另一个重要原因是,由于它的异步特性,后面的状态更新可能会覆盖直接对状态所做的更新,从而导致一些无法查清的错误。在这种情况下,正确的方法是使用useState的setter方法。

 const updateFeaturesList = (e, idx) => {
const { checked } = e.target;
setListFeatures(features => {
return features.map((feature, index) => {
if (id === index) {
feature = { ...feature, checked };
}
return feature;
});
});
};

通过使用map和object spread(对象展开),我们还能确保不会更改原始状态项。

在初始状态上设置错误的值类型

将初始状态值设置为或空字符串,然后在render方法中访问该值的属性,就好像访问一个对象一样,这是一种很常见的错误。同样的常见错误还有,不为嵌套对象提供默认值,然后尝试在render方法或其他组件方法中访问它们。

class UserProfile extends Component {
constructor(props) {
super(props);

this.state = {
user:
};
}

componentDidMount {
fetch("/api/profile").then(data => {
this.setState({ user: data });
});
}

render {
return (
<div>
<p>User name:</p>
<p>{this.state.user.name}</p> // Cannnot read property 'name' of
</div>
);
}
}

如果将初始状态的值设置为空数组,然后尝试访问这个空数组中的第n个项,也会发生类似的错误。当通过API调用来获取数据时,组件将以提供的初始状态渲染,并且尝试访问或未定义元素上的属性,这也将导致错误。因此,让初始状态立即被更新,这一点很重要。

在我们的例子里,正确的状态初始化应该像下面这样:

 class UserProfile extends Component {
constructor(props) {
super(props);

this.state = {
user: {
name: ""
// Define other fields as well
}
};
}

componentDidMount {
fetch("/api/profile").then(data => {
this.setState({ user: data });
});
}

render {
return (
<div>
<p>User name:</p>
<p>{this.state.user.name}</p> // Renders without errors
</div>
);
}
}

从用户体验的角度来看,最好展示某种loader的结果给用户,直到数据被正确地获取到。

忘记setState是异步的

另一个常见的错误是试图在设置状态值之后立即访问它。

 handleChange = count => {
this.setState({ count });
this.props.callback(this.state.count); // Old state value
};

设置新值不会立即生效,通常它会在下一个可用的渲染上完成,或者可以进行批量处理以优化性能。因此,在设置状态值之后立即访问该值可能不会得到最新的更新结果。这个问题可以通过使用setState的可选的第二个参数来解决,这个参数是一个回调函数,它在状态值被最新的值更新完成后会被调用。

 handleChange = count => {
this.setState({ count },  => {
this.props.callback(this.state.count); // Updated state value
});
};

不过,这与钩子(hook)的做法有很大不同,因为useState钩子的setter函数没有第二个类似于setState的回调参数。在这种情况下,官方推荐的做法是使用useEffect钩子。

 const [count, setCount] = useState(0)

useEffect( => {
callback(count); // Will be called when the value of count changes
}, [count, callback]);

const handleChange = value => {
setCount(value)
};

应该注意的是,setState方法严格来说并不是异步的,只不过它返回的是一个预期(promise)。因此,对它进行async/await操作或使用then将不起作用(这是另一个常见的错误)。

错误地依赖当前状态值来计算下一个状态

这个问题与上面讨论的问题有关,因为它还是和异步状态更新相关。见下例:

 handleChange = count => {
this.setState({ count: this.state.count + 1 }); // Relying on current value of the state to update it
};

上面代码中的这种更新方式存在的问题是:在设置新状态时,count的值可能没有正确更新,这将导致新状态值的设置不正确。正确的方法是使用setState的函数形式。

 increment =  => {
this.setState(state => ({ count: state.count + 1 })); // The latest state value is used
};

setState的函数形式在更新被执行时有第二个参数 - props,可以以和state参数类似的方式使用。

同样的逻辑也适用于useState钩子,其中setter接受函数作为参数。

 const increment =  => {
setCount(currentCount => currentCount + 1)
};

忽略useEffect的dependency数组

这是一个不太常见的错误,但仍然时有发生。即使有完全有效的情况可以忽略useEffect的dependency数组,但在其回调函数更新状态时这样做可能会导致无限循环。

将非基元类型的对象或其它值传递给useEffect的dependency数组

与上面的情况类似,但更微妙的错误是跟踪对象、数组或effect钩子的dependency数组中的其他非基元值。考虑下面的代码:

 const features = ["feature1", "feature2"];
useEffect( => {
// Callback
}, [features]);

在这里,当我们将数组作为一个dependency数组传递时,React将只存储对它的引用,并将其与数组的上一个引用进行比较。但是,由于它是在组件内部声明的,因此在每次渲染时都会重新创建features数组,这意味着它的引用每次都是新的,因此不等于useEffect跟踪的引用。最终,即使数组没有被更改,回调函数也会在每个render方法上运行。对于基元类型的值(如字符串和数字)来说,这不是问题,因为它们在JavaScript中是按值来比较的,而不是按引用来比较。

有几种方法可以解决这个问题。第一个方法是将变量声明移到组件之外,这样就不会在每次渲染时重新创建它。但是,在某些情况下,这是不可能的,例如,如果我们正在跟踪的props,或者跟踪的依赖项是组件状态的一部分。另一种方法是使用自定义的deep compare hook来正确地跟踪依赖项引用。而更简单的解决方法是将值包装到usememohook中,这种做法会在重新渲染期间保留引用。

 const features = useMemo( => ["feature1", "feature2"], );

useEffect( => {
// Callback
}, [features]);

希望上面的这个列表能够帮助你避免最常见的React使用问题,并提高对主要问题的理解。

如果你有关于这篇文章的任何问题/评论或其他类型的反馈,请在此评论或在推特上告诉我。

原文:https://dev.to/clarity89/the-most-common-mistakes-when-using-react-45h2

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

热门产品

php编程基础教程.pptx|php编程培训,php,编程,基础,教程,pptx
php编程基础教程.pptx

历史上的今天:04月29日

热门专题

云南综合高中|云南综合高中
云南综合高中
APP开发|app开发_app开发公司_app软件开发_专业app开发_云南app开发公司_app定制_原生app开发定制
APP开发
卓越综合高中|卓越综合高中
卓越综合高中
大理科技管理学校|大理科技管理中等职业技术学校,大理市科技管理中等职业技术学校
大理科技管理学校
天麻的功效与作用吃法|天麻的功效与作用,天麻的功效与作用吃法,天麻炖什么治头痛最好,天麻的功效与作用禁忌,天麻多少钱一斤,天麻的功效与作用吃法及禁忌,天麻怎么吃效果最好,天麻粉的功效与作用,天麻怎么吃
天麻的功效与作用吃法
云南开放大学|云南开放大学报名,云南开放大学报考,云南开放大学,什么是云南开放大学,云南开放大学学历,云南开放大学学费,云南开放大学报名条件,云南开放大学报名时间,云南开放大学学历,云南开放大学专业
云南开放大学
中源管业|中源管业,中源管业公司,中源管业有限公司,中源管业电话,中源管业地址,中源管业电力管,中源管业mpp电力管,中源管业cpvc电力管,中源管业pe穿线管
中源管业
安徽中源管业|安徽中源管业,安徽中源管业mpp电力管,安徽中源管业cpvc电力管,安徽中源管业pe穿线管,安徽中源管业电力管,安徽中源管业排水管,安徽中源管业通信管,安徽中源管业管材
安徽中源管业

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部