闽公网安备 35020302035485号
function ProfilePage(props) {
// 堆代码 duidaima.com
const showMessage = () => alert('你好 ' + props.user);
const handleClick = () => setTimeout(showMessage, 3000);
return <button onClick={handleClick}>Follow</button>
}
上述组件:如果 props.user是 Dan,它会在三秒后显示 你好 Dan。如果是类组件我们怎么写?一个简单的重构可能就象这样:class ProfilePage extends React.Component {
showMessage = () => alert('Followed ' + this.props.user);
handleClick = () => setTimeout(this.showMessage, 3000);
render() {
return <button onClick={this.handleClick}>Follow</button>;
}
}
通常我们认为,这两个代码片段是等效的。人们经常在这两种模式中自由的重构代码,但是很少注意到它们的含义:3.查看 弹出的文本。
showMessage = () => {
alert('Followed ' + this.props.user);
};
这个类方法从 this.props.user 中读取数据。在 React 中 Props 是 不可变(immutable)的,所以他们永远不会改变。而 this 是而且永远是 可变(mutable)的。**class ProfilePage extends React.Component {
showMessage = (user) => alert('Followed ' + user);
handleClick = () => {
const {user} = this.props;
setTimeout(() => this.showMessage(user), 3000);
};
render() {
return <button onClick={this.handleClick}>Followbutton>;
}
}
然而,这种方法使得代码明显变得更加冗长。如果我们需要的不止是一个props 该怎么办?如果我们还需要访问state 又该怎么办?如果 showMessage 调用了另一个方法,然后那个方法中读取了 this.props.something 或者 this.state.something ,我们又将遇到同样的问题。然后我们不得不将 this.props和 this.state以函数参数的形式在被 showMessage调用的每个方法中一路传递下去。class ProfilePage extends React.Component {
render() {
const props = this.props;
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return <button onClick={handleClick}>Follow</button>;
}
}
你在渲染的时候就已经"捕获"了props:。这样,在它内部的任何代码(包括 showMessage)都保证可以得到这一次特定渲染所使用的props。function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
就像上面这样, props仍旧被捕获了 —— React将它们作为参数传递。不同于 this , props 对象本身永远不会被React改变。当父组件使用不同的props来渲染 ProfilePage时,React会再次调用 ProfilePage函数。但是我们点击的事件处理函数,"属于"具有自己的 user值的上一次渲染,并且 showMessage回调函数也能读取到这个值。它们都保持完好无损。function MessageThread() {
const [message, setMessage] = useState('');
const showMessage = () => {
alert('You said: ' + message);
};
const handleSendClick = () => {
setTimeout(showMessage, 3000);
};
const handleMessageChange = (e) => {
setMessage(e.target.value);
};
return <>
<input value={message} onChange={handleMessageChange} />
<button onClick={handleSendClick}>Send</button>
</>;
}
如果我发送一条特定的消息,组件不应该对实际发送的是哪条消息感到困惑。这个函数组件的 message变量捕获了"属于"返回了被浏览器调用的单击处理函数的那一次渲染。所以当我点击"发送"时 message被设置为那一刻在input中输入的内容。function MyComponent() {
const ref = useRef(null);
}
但是,你必须自己管理它。一个ref与一个实例字段扮演同样的角色。这是进入可变的命令式的世界的后门。你可能熟悉'DOM refs',但是ref在概念上更为广泛通用。它只是一个你可以放东西进去的盒子。function MessageThread() {
const [message, setMessage] = useState('');
const latestMessage = useRef('');
const showMessage = () => {
alert('You said: ' + latestMessage.current); };
const handleSendClick = () => {
setTimeout(showMessage, 3000);
};
const handleMessageChange = (e) => {
setMessage(e.target.value);
latestMessage.current = e.target.value; };
如果我们在 showMessage中读取 message,我们将得到在我们按下发送按钮那一刻的信息。但是当我们读取 latestMessage.current,我们将得到最新的值 —— 即使我们在按下发送按钮后继续输入。function MessageThread() {
const [message, setMessage] = useState('');
const latestMessage = useRef('');
useEffect(() => {
latestMessage.current = message;
});
const showMessage = () => {
alert('You said: ' + latestMessage.current);
};
我们在一个effect 内部执行赋值操作以便让ref的值只会在DOM被更新后才会改变。这确保了我们的变量突变不会破坏依赖于可中断渲染的时间切片和 Suspense 等特性。通常来说使用这样的ref并不是非常地必要。捕获props和state通常是更好的默认值。 然而,在处理类似于intervals和订阅这样的命令式API时,ref会十分便利。你可以像这样跟踪 任何值 —— 一个prop,一个state变量,整个props对象,或者甚至一个函数。