对 React 组件中的 Prop 变化做出反应
介绍
使用 React 构建组件时,通常需要在组件属性之一发生变化时创建副作用。这可能是调用 API 来获取一些数据、操作 DOM、更新某些组件状态或任何其他操作。本指南将展示使用React 钩子和类组件在功能组件中创建这些副作用的方法。
起始组件
本指南将从一个非常简单的标签组件开始,该组件将具有一个名为text的 prop并将其显示在span内,然后扩展此组件以在父组件更改 prop 时突出显示文本。文本突出显示的实现将组件状态设置为背景颜色,设置一秒的超时,并将状态设置回原始背景颜色。
启动组件的代码如下所示:
function Label({text}) {
return <span className="label-text">{text}</span>;
}
功能组件
在 16.8 版本中,引入了 React hooks。Hooks 允许将组件构建为函数,而无需类。
此组件需要一个状态变量来跟踪背景颜色,以及一个 ref 来存储当前计时器实例。尽管 ref 主要用于访问 DOM,但useRef钩子也可用于存储可变变量,该变量在更改时不会触发组件的更新。它还需要一个函数来将状态设置为颜色,等待一秒钟,然后将其设置回默认值。组件返回的标记将与原始标签相同,但增加了样式设置。完成所有这些操作的代码如下:
function Label({text}) {
const [backgroundColor, setBackgroundColor] = React.useState("inherit");
const updateTimer = React.useRef(null);
function setUpdate() {
setBackgroundColor("#9b34ee");
updateTimer.current = setTimeout(() => {
setBackgroundColor("inherit");
updateTimer.current = null;
}, 1000);
}
return (
<span className="label-text" style={{ backgroundColor: backgroundColour }}>
{text}
</span>
);
}
效果钩子
现在组件已准备好创建高亮副作用,只需在文本属性发生更改时调用setUpdate函数即可。在 hooks API 中执行此操作的方法是使用效果钩子。
效果钩子可以在每次渲染后运行,也可以仅在挂载和卸载时运行,或者在指定值发生更改时运行。它需要两个参数 - 第一个是要执行的函数,第二个是可选的变量数组,当更改时,将触发函数的执行。
如果调用钩子时没有第二个参数,那么每次更新组件时都会调用该函数,如下所示:
React.useEffect(() => { console.log("component updated"); });
此组件将需要两个效果挂钩。第一个仅在文本道具已更新时调用,并将检查是否没有正在进行的突出显示,如果没有,则将调用上一节中定义的setUpdate函数:
React.useEffect(() => {
if(!updateTimer.current) {
setUpdate();
}
}, [text]);
此组件所需的第二个效果钩子用于在组件卸载时清理计时器引用。这可以通过传递一个空数组作为第二个参数并返回一个函数来实现,该函数将在组件卸载时调用。此代码如下所示:
React.useEffect(()=> {
return () => {
if (updateTimer.current) {
clearTimeout(updateTimer.current);
}
};
}, []);
将这两个对useEffect的调用添加到 Label 函数中,每当文本属性发生变化时,背景颜色就会突出显示一秒钟。
类组件
本指南将探讨使用类组件实现突出显示的两种方法。 两者都需要一个扩展Component 的类、一个名为updateAndNotify的方法(该方法将设置背景颜色状态,然后在一秒后将其设置回初始值)、一个componentWillUnmount' 方法来清除计时器,以及一个render` 方法。 此代码如下所示:
class Label extends React.Component {
state = { backgroundColour: "inherit" };
componentWillUnmount() {
if (this.updateTimer) {
clearTimeout(this.updateTimer);
}
}
updateAndNotify = () => {
if (this.updateTimer) return;
this.setState({ backgroundColour: "#9b34ee" });
this.updateTimer = setTimeout(() => {
this.setState({ backgroundColour: "inherit" });
this.updateTimer = null;
}, 1000);
}
render() {
return (
<span className="label-text" style={{ backgroundColor: this.state.backgroundColour }}>
{this.props.text}
</span>
);
}
}
组件更新
每当类组件更新时,都会调用componentDidUpdate方法。此方法的第一个参数是更新前的 props;因此,可以在此处测试 prop 是否已更改。对于此组件,该方法将如下所示:
componentDidUpdate(prevProps) {
if (prevProps.text !== this.props.text) {
this.updateAndNotify();
}
}
上面只是测试prevProps.text是否与this.props.text不同,如果不同,则调用方法来更新状态。
使用eslint时,您可能会看到有关在componentDidUpdate方法中设置状态的错误或警告。在没有任何保护的情况下在此方法中设置状态可能会导致无限循环,在这种情况下,我们仅在文本属性已更改时设置状态;所以没问题。
获取更新前快照
getSnapshotBeforeUpdate方法还可用于测试 props 的更改。此方法在组件生命周期中的componentDidUpdate方法之前调用,并返回一个值,该值作为第三个参数传递给componentDidUpdate方法。要实现突出显示组件, getSnapshotBeforeUpdate方法应返回一个对象,其中包含一个设置为 true 或 false 的字段,具体取决于文本 prop 是否已更改,然后componentDidUpdate方法应检查该字段,如果为 true,则调用该方法更新状态。像这样:
getSnapshotBeforeUpdate(prevProps) {
return { notifyRequired: prevProps.text !== this.props.text };
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot.notifyRequired) {
this.updateAndNotify();
}
}
这个用例不是getSnapshotBeforeUpdate的通常使用方式;因为在componentDidUpdate方法中执行所有操作更有意义,但这是为了说明此方法可用于识别 prop 变化。
结论
使用 React 向函数式和类组件添加副作用相对简单。本指南中组件的代码可在此处找到。
了解更多
探索 Pluralsight 的这些 React 课程以继续学习:
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~