React Hooks:在不更改事件处理程序函数引用的情况下跨函数访问状态

asl*_*rai 8 javascript reactjs 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()} />
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

为了简化我的观点,这是一个人为的示例。我本质上想要做的是保持对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()} />
    );
}
Run Code Online (Sandbox Code Playgroud)

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

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

进行上述任何一项操作都会更改对我需要维护的功能的常量引用,以便不input重新渲染组件。我该如何使用挂钩?可能吗

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

编辑:这不是React中useCallback做什么的重复项因为我试图找出如何实现与类组件方式相似的效果,并且尽管useCallback提供了一种实现方式,但对于可维护性问题而言,它并不是理想的选择。

Moh*_*ami 7

在组件函数中定义函数,并使用闭包传递值。然后,您正在寻找的是useCallback避免不必要的重新渲染。(对于此示例,它不是很有用)

function SomeComponent(props) {
  const [text, setText] = useState('');

  const onChange = useCallback((ev)  => {
    setText(ev.target.value);
  }, []);

  function transformText(text) {
    return text.toUpperCase();
  };

  return (
    <input type="text" onChange={onChange} value={transformText(text)} />
  );
}
Run Code Online (Sandbox Code Playgroud)

在这里阅读更多


dan*_*die 7

在这里您可以构建自己的钩子(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} />
  );
}
Run Code Online (Sandbox Code Playgroud)

并将其粘贴到新函数中(按照惯例,前缀为“ use *”)。命名状态和回调以返回(根据您的情况作为对象或数组)

function useTransformedText(textTransformer = text => text.toUpperCase()) {
  const [text, setText] = React.useState("");

  const onChange = ev => {
    setText(ev.target.value);
  };

  return { onChange, text: textTransformer(text) };
}
Run Code Online (Sandbox Code Playgroud)

由于可以传递转换逻辑(但默认情况下使用UpperCase),因此可以使用自己的钩子使用共享逻辑。

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} />
  );
}
Run Code Online (Sandbox Code Playgroud)

您可以像下面这样使用上面的组件。

function App() {
  return (
    <div className="App">
      To Upper case: <UpperCaseInput />
      <br />
      To Lower case: <LowerCaseInput />
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

结果看起来像这样。

结果演示

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

  • 需要在更多函数中访问“文本”是什么,而不能将其放置在“ useTransformedText”挂钩中?当将`text`作为参数传递给这些函数时,他们看不到其中的更改... (2认同)