首页 > 解决方案 > React Hooks:跨函数访问状态而不更改事件处理函数引用

问题描述

在基于类的 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 重新渲染我的组件时,如果输入值没有改变,它不会重新渲染输入。

这里需要注意的重要事项:

  1. this.onChange 是对同一函数的常量引用。
  2. this.onChange 需要能够访问状态设置器(在本例中为 this.setState)

现在,如果我要使用钩子重写这个组件:

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()} />
    );
}

现在的问题是我需要分别传递texttransformText和传递setTextonChange方法。我能想到的可能解决方案是:

  1. 在组件函数中定义函数,并使用闭包来传递值。
  2. 在组件函数内部,将值绑定到方法,然后使用绑定的方法。

执行其中任何一项都会更改对我需要维护的函数的常量引用,以免input组件重新渲染。我如何用钩子做到这一点?甚至可能吗?

请注意,这是一个非常简化的、人为的示例。我的实际用例非常复杂,我绝对不想不必要地重新渲染组件。

编辑:这不是什么 useCallback 在 React 中做的重复?因为我试图弄清楚如何实现与过去以类组件方式完成的效果类似的效果,并且虽然useCallback提供了一种实现方式,但它对于可维护性问题并不理想。

标签: javascriptreactjsreact-hooks

解决方案


这是您可以构建自己的钩子的地方(Dan Abramov 敦促不要使用术语“自定义钩子”,因为它使创建自己的钩子比它更难/更高级,这只是复制/粘贴您的逻辑)提取文本转换逻辑

只需从Mohamed 的回答中“剪切”下面注释掉的代码即可。

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>
  );
}

结果看起来像这样。

结果演示

您可以在此处运行工作代码。
编辑 so.answer.54597527


推荐阅读