@程序员,如何更好地写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(对象展开),我们还能确保不会更改原始状态项。

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

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

class UserProfile extends Component {constructor(props) {super(props);this.state = {user: null};}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 null</div>);}}

如果将初始状态的值设置为空数组,然后尝试访问这个空数组中的第n个项,也会发生类似的错误。当通过API调用来获取数据时,组件将以提供的初始状态渲染,并且尝试访问null或未定义元素上的属性,这也将导致错误。因此,让初始状态立即被更新,这一点很重要。
在我们的例子里,正确的状态初始化应该像下面这样:
 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

本文为 CSDN 翻译,转载请注明来源出处。

【END】

Python学习方法Python入门须知!(内附python教程分享)

https://edu.csdn.net/topic/python115?utm_source=csdn_bw

CSDN 博客诚邀入驻啦!

本着共享、协作、开源、技术之路我们共同进步的准则,

只要你技术够干货,内容够扎实,分享够积极,

欢迎加入 CSDN 大家庭!

扫描下方二维码,即刻加入吧!

 热 文 推 荐 

Python 分析热门旅游景点,告诉你哪些地方好玩、便宜、人又少!
点击阅读原文,即刻阅读《程序员大本营》最新期刊。
你点的每个“在看”,我都认真当成了喜欢

@程序员,React 使用如何避坑?相关推荐

  1. 程序员跳槽找工作避坑指南

    虽说年前就有很多人在修改简历.刷新简历,但是为了大家心中所谓的年终奖,99.9999%的人都会选择沉默,有一种「明修栈道,暗度陈仓」意味. 拿完了年终奖,过完了春节,老子早TMD的不想干了,终于不用在 ...

  2. 程序员跳槽找工作避坑指南(2019最新新版)

    虽说年前就有很多人在修改简历.刷新简历,但是为了大家心中所谓的年终奖,99.9999%的人都会选择沉默,有一种「明修栈道,暗度陈仓」意味. 拿完了年终奖,过完了春节,老子早TMD的不想干了,终于不用在 ...

  3. 小白程序员最容易踩的“坑”,你踩过几个?

    人生,就是一边踩"坑",一边上升的过程.而程序员的一生,不仅要改无数的BUG,也要越过很多的"坑".今天,下面为大家分享一些开发人员常见的"坑&quo ...

  4. @程序员,敏捷开发防坑指南请查收!

    作者 | 弯月 责编 | 郭芮 随着市场的瞬息万变和软件行业的迅猛发展,传统的瀑布式软件开发模型因其漫长的开发与反馈周期,在抢占市场先机和快速满足用户需求方面日渐失去竞争优势.与此同时,敏捷开发以其快 ...

  5. 必看!前辈们总结出的程序员找工作遇到的坑

    最近不管是裁员潮,还是被炒的火热的 996 有赞事件,感觉大家都不容易,今天给大家推荐的就是程序员找工作黑名单的开源库.(结尾带链接) 程序员给人的印象就是:宅,不爱说话,埋头苦敲.所以,不管什么事件 ...

  6. 2020最难求职年,程序员职场面试 “防坑指南” ,全员必备!

    [文章来源微信公众号:每天学编程] 今年是最难求职年,我希望通过这篇文章能帮大家提高求职成功率.这篇文章分为简历篇.面试篇.谈薪酬篇,包括了找工作过程中各个环节的技巧和防坑指南. 1. 简历篇 如果把 ...

  7. 《程序员入职甲乙方公司的套路》。送给要入职的程序员们,避免踩坑。

    程序员入职时的套路 以下所说的只代表我入职中所有遇到的,并不代表所有,只能给大家提供参考经验.以及在入职过程中避免踏入这些坑. 一.个人的经历 本人是17年毕业,最开始在郑州的某公司待了两年,那时候刚 ...

  8. 程序员找工作的各种坑…… 及防坑指南

    我原来面试过一个哥们儿,半年内换了七家公司,我表示膜拜. 还有一些哥们儿,七年不换工作.这得是多稳定的工作啊,七年之痒都扛过去了,真心不错. 这都是极端,一般的程序员,可能会一年半载或者三两年换一次工 ...

  9. 作为程序员,你是填坑的还是挖坑的?

    你是否经常遇到这样的情景:负责开发的项目遇到线上bug,心想这不是我的锅,先不管了,放着吧:代码写完后,隐隐感觉有问题,可程序跑得通,先用着吧:接手一个老系统,这什么破代码,算了,改吧改吧将就用吧-- ...

最新文章

  1. 好用到哭!你需要立刻学会的20个Python代码段
  2. nRF51822 SDK初体验
  3. 开关灯(jzoj 3926)
  4. C++(18)--复制构造函数
  5. vba 跳到下一个循环_遍历工作薄和工作表(For Each循环的利用)
  6. 根据工序画出aoe网_这些金刚网纱窗竟然含“毒”!选错就得病
  7. Ajax Login Sample
  8. 两位小数乘两位小数竖式_冀教版五年级数学上册2.4小数乘小数微课视频 | 练习...
  9. 关于Adobe软件安装失败的各类错误代码BUG汇总!!
  10. 工业标准接口OPC Server
  11. nc 单据模板公式
  12. Verilog——三角波发生器(状态机)
  13. Python物理建模初学者指南
  14. linux下安装mysql8.0(二进制方式)
  15. 网络工程师必知:网关与防火墙有什么区别?
  16. 美业选择会员系统的原因
  17. 深度强化学习——actor-critic算法(4)
  18. IE6/7常用的hack
  19. 【C语言刷题】青蛙跳台阶
  20. VIM_readme

热门文章

  1. Gitbook 常用命令
  2. 《Linux编程》上机作业 ·003【Shell编程】
  3. mysqlli php7.0_php7配置mysqli并使用mysqli连接mysql
  4. matlab mingw 32,Matlab 2017b MinGW-w64 5.3安裝
  5. hdfs java 权限管理,HDFS的权限管理
  6. windows聚焦图片为什么不更新了_网站内容更新,相同内容,不同网站为什么排名不一样?...
  7. 记录——《C Primer Plus (第五版)》第七章编程练习第九题
  8. kafka保证数据可靠性的方式
  9. Flutter进阶—实现动画效果(七)
  10. 2021-2025年中国乙酰丙酸乙酯行业市场供需与战略研究报告