@程序员,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 条评论

暂无评论

热门产品

历史上的今天:03月29日

热门专题

安徽中源管业有限公司|安徽中源管业有限公司,安徽中源管业有限公司介绍,安徽中源管业有限公司电话,安徽中源管业有限公司地址,安徽中源管业有限公司厂家,安徽中源管业有限公司电力管,安徽中源管业有限公司管材
安徽中源管业有限公司
云南开放大学|云南开放大学报名,云南开放大学报考,云南开放大学,什么是云南开放大学,云南开放大学学历,云南开放大学学费,云南开放大学报名条件,云南开放大学报名时间,云南开放大学学历,云南开放大学专业
云南开放大学
易捷尔高职单招|易捷尔高职单招,易捷尔高职单招培训,单招分数线,单招录取分数线,高职单招学校分数线
易捷尔高职单招
昆明网站建设|昆明网站建设,昆明网站开发,昆明网站建设公司,昆明网站建设价格,昆明网站设计,昆明网站制作,网页设计,高端网站建设,高端网站设计
昆明网站建设
大理科技管理学校|大理科技管理学校,大理科技,大理科技中等职业技术学校,大理科技管理中等职业技术学校,大理科技学校
大理科技管理学校
安徽中源管业|安徽中源管业,安徽中源管业mpp电力管,安徽中源管业cpvc电力管,安徽中源管业pe穿线管,安徽中源管业电力管,安徽中源管业排水管,安徽中源管业通信管,安徽中源管业管材
安徽中源管业
天麻的功效与作用吃法|天麻的功效与作用,天麻的功效与作用吃法,天麻炖什么治头痛最好,天麻的功效与作用禁忌,天麻多少钱一斤,天麻的功效与作用吃法及禁忌,天麻怎么吃效果最好,天麻粉的功效与作用,天麻怎么吃
天麻的功效与作用吃法
小程序开发|微信小程序,小程序开发,小程序,小程序制作,微信小程序开发,小程序公司,小程序开发公司,分销,三级分销系统,分销系统
小程序开发

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部