[英]React Hooks: accessing state across functions without changing event handler function references
在基于类的 React 组件中,我执行以下操作:
class SomeComponent extends React.Component{
onChange(ev){
this.setState({text: ev.currentValue.text});
}
transformText(){
return this.state.text.toUpperCase();
}
render(){
return (
<input type="text" onChange={this.onChange} value={this.transformText()} />
);
}
}
这是一个人为的例子来简化我的观点。 我本质上想要做的是保持对 onChange 函数的常量引用。 在上面的例子中,当 React 重新渲染我的组件时,如果输入值没有改变,它不会重新渲染输入。
这里需要注意的重要事项:
现在,如果我要使用钩子重写这个组件:
function onChange(setText, ev) {
setText(ev.currentValue.text);
};
function transformText(text) {
return text.toUpperCase();
};
function SomeComponent(props) {
const [text, setText] = useState('');
return (
<input type="text" onChange={onChange} value={transformText()} />
);
}
现在的问题是我需要分别将text
传递给transformText
和setText
到onChange
方法。 我能想到的可能解决方案是:
执行这些操作中的任何一个都会更改对我需要维护的函数的常量引用,以便不重新渲染input
组件。 我如何用钩子做到这一点? 甚至有可能吗?
请注意,这是一个非常简化、人为设计的示例。 我的实际用例非常复杂,我绝对不想不必要地重新渲染组件。
编辑:这不是什么 useCallback 在 React 中做的重复? 因为我试图弄清楚如何实现与过去在类组件方式中所做的类似的效果,虽然useCallback
提供了一种方法,但它对于可维护性问题并不理想。
在组件函数内定义回调,并使用闭包传递值。 那么您正在寻找的是useCallback
钩子,以避免不必要的重新渲染。 (对于这个例子,它不是很有用)
function transformText(text) {
return text.toUpperCase();
};
function SomeComponent(props) {
const [text, setText] = useState('');
const onChange = useCallback((ev) => {
setText(ev.target.value);
}, []);
return (
<input type="text" onChange={onChange} value={transformText(text)} />
);
}
在这里阅读更多
在这里您可以构建自己的钩子(Dan Abramov 敦促不要使用术语“自定义钩子”,因为它使创建自己的钩子比它更难/更高级,这只是复制/粘贴您的逻辑)提取文本转换逻辑
只需从Mohamed's answer 中“剪切”下面注释掉的代码。
function SomeComponent(props) {
// const [text, setText] = React.useState("");
// const onChange = ev => {
// setText(ev.target.value);
// };
// function transformText(text) {
// return text.toUpperCase();
// }
const { onChange, text } = useTransformedText();
return (
<input type="text" onChange={React.useCallback(onChange)} value={text} />
);
}
并将其粘贴到一个新函数中(按照惯例,前缀为“use*”)。 命名要返回的状态和回调(根据您的情况作为对象或数组)
function useTransformedText(textTransformer = text => text.toUpperCase()) {
const [text, setText] = React.useState("");
const onChange = ev => {
setText(ev.target.value);
};
return { onChange, text: textTransformer(text) };
}
由于可以传递转换逻辑(但默认使用大写),您可以使用自己的钩子使用共享逻辑。
function UpperCaseInput(props) {
const { onChange, text } = useTransformedText();
return (
<input type="text" onChange={React.useCallback(onChange)} value={text} />
);
}
function LowerCaseInput(props) {
const { onChange, text } = useTransformedText(text => text.toLowerCase());
return (
<input type="text" onChange={React.useCallback(onChange)} value={text} />
);
}
您可以使用上述组件,如下所示。
function App() {
return (
<div className="App">
To Upper case: <UpperCaseInput />
<br />
To Lower case: <LowerCaseInput />
</div>
);
}
结果看起来像这样。
这种情况不是特定于钩子的,如果应该从类中提取transformText
和onChange
,则类组件和setState
将相同。 不需要提取一行函数,因此可以假设实际函数足够复杂以证明提取是合理的。
拥有接受值作为参数的转换函数是完全没问题的。
至于事件处理程序,它应该有一个对setState
的引用,这限制了它的使用方式。
一个常见的方法是使用状态更新器功能。 如果需要接受附加值(例如事件值),则应该是高阶函数。
const transformText = text => text.toUpperCase();
const onChange = val => _prevState => ({ text: val });
function SomeComponent(props) {
const [text, setText] = useState('');
return (
<input type="text" onChange={e => setText(onChange(e.currentValue.text)} value={transformText(text)} />
);
}
这个秘籍在这种情况下看起来没什么用,因为原来的onChange
没有多大用处。 这也意味着提取是不合理的。
与this.setState
相比,特定于钩子的一种方式是setText
可以作为回调传递。 所以onChange
可以是高阶函数:
const transformText = text => text.toUpperCase();
const onChange = setState => e => setState({ text: e.currentValue.text });
function SomeComponent(props) {
const [text, setText] = useState('');
return (
<input type="text" onChange={onChange(setText)} value={transformText(text)} />
);
}
如果目的是减少因在改变儿童重新呈现onChange
道具, onChange
应该与memoized useCallback
或useMemo
。 这是可能的,因为useState
setter 函数在组件更新之间不会改变:
...
function SomeComponent(props) {
const [text, setText] = useState('');
const memoizedOnChange = useMemo(() => onChange(setText), []);
return (
<input type="text" onChange={memoizedOnChange} value={transformText(text)} />
);
}
同样的事情可以通过不提取onChange
和使用useCallback
来实现:
...
function SomeComponent(props) {
const [text, setText] = useState('');
const onChange = e => setText({ text: e.currentValue.text });
const memoizedOnChange = useCallback(onChange, []);
return (
<input type="text" onChange={memoizedOnChange} value={transformText(text)} />
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.