如何在react中使用带有 useState 钩子的回调

IT技术 reactjs react-hooks
2021-04-10 01:03:07

我正在使用带有钩子的功能组件。我需要从孩子更新父母的状态。我在 Parent 中使用了一个 prop 函数。一切正常,除了我的 prop 函数正在获取以前的状态而不是当前状态。我的 prop 函数在useState钩子设置当前状态之前执行如何在 useState 调用后等待我的回调函数执行。我正在从基于类的组件中寻找类似setState(state,callback) 的东西

这是代码片段:

function Parent() {
  const [Name, setName] = useState("");
  getChildChange = getChildChange.bind(this);
  function getChildChange(value) {
    setName(value);
  }

  return <div> {Name} :
    <Child getChildChange={getChildChange} ></Child>
  </div>
}

function Child(props) {
  const [Name, setName] = useState("");
  handleChange = handleChange.bind(this);

  function handleChange(ele) {
    setName(ele.target.value);
    props.getChildChange(collectState());
  }

  function collectState() {
    return Name;
  }

  return (<div>
    <input onChange={handleChange} value={Name}></input>
  </div>);
} 
6个回答

您可以使用 useEffect/useLayoutEffect 来实现这一点:

const SomeComponent = () => {
  const [count, setCount] = React.useState(0)

  React.useEffect(() => {
    if (count > 1) {
      document.title = 'Threshold of over 1 reached.';
    } else {
      document.title = 'No threshold reached.';
    }
  }, [count]);

  return (
    <div>
      <p>{count}</p>

      <button type="button" onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
};

更多关于它在这里

如果您正在寻找开箱即用的解决方案,请查看这个自定义钩子,它的工作方式类似于 useState 但接受回调函数作为第二个参数:

// npm install use-state-with-callback

import useStateWithCallback from 'use-state-with-callback';

const SomeOtherComponent = () => {
  const [count, setCount] = useStateWithCallback(0, count => {
    if (count > 1) {
      document.title = 'Threshold of over 1 reached.';
    } else {
      document.title = 'No threshold reached.';
    }
  });

  return (
    <div>
      <p>{count}</p>

      <button type="button" onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
};
我正在尝试安装 use-state-with-callback 但它不起作用。它给出了一个错误。我能做什么?
2021-06-04 01:03:07
一个非常好的方法。我发现它非常有用
2021-06-16 01:03:07

setState(updater, callback) 为了 useState

以下实现非常接近于setState的原始回调。

接受的答案所做的改进

  1. 初始渲染时省略了回调执行 - 我们只想在状态更新时调用它
  2. 每次setState调用的回调都可以是动态的,就像类一样

用法

const App = () => {
  const [state, setState] = useStateCallback(0); // same API as useState

  const handleClick = () => {
    setState(
      prev => prev + 1,
      // second argument is callback, `s` being the *updated* state
      s => console.log("I am called after setState, state:", s)
    );
  };

  return <button onClick={handleClick}>Increment</button>;
}

useStateCallback

function useStateCallback(initialState) {
  const [state, setState] = useState(initialState);
  const cbRef = useRef(null); // init mutable ref container for callbacks

  const setStateCallback = useCallback((state, cb) => {
    cbRef.current = cb; // store current, passed callback in ref
    setState(state);
  }, []); // keep object reference stable, exactly like `useState`

  useEffect(() => {
    // cb.current is `null` on initial render, 
    // so we only invoke callback on state *updates*
    if (cbRef.current) {
      cbRef.current(state);
      cbRef.current = null; // reset callback after execution
    }
  }, [state]);

  return [state, setStateCallback];
}

更多信息:React Hooks 常见问题解答:是否有类似实例变量的东西?

工作示例

@PetrÚjezdský 感谢您的想法!回复 1:我想,这个评论很合适。Re 2:如果你setState同一个渲染周期和同一个钩子实例中调用两次,最后一个值在 React 中获胜。因此,如果同时调用回调新回调,我希望在设置回调时出现相同的行为,并且宁愿感到困惑无论如何,这两种情况似乎都是一种边缘情况 - 很可能你会有一个事件处理程序,其中设置状态在不同的渲染中完成
2021-05-23 01:03:07
@dwjohnston在相同值的情况下退出状态更新是 Hooks 的新 React 默认值 - 因此在大多数情况下我不会改变这种行为。如果由于遗留原因(由于对象合并而出现这种行为),您真的需要与旧的基于类的比较内联,那么代码和框方法看起来很合理!除了使用Symbol,您还不如每次都将状态值包装在一个新的对象容器中。
2021-05-28 01:03:07
@bot19 这是回调的实际调用,之前已通过setState(..,cb). cbRef.current存储一个函数。然后(state)使用当前更新的状态调用此函数
2021-06-04 01:03:07
考虑把它变成一个 npm 包!
2021-06-04 01:03:07
是什么cbRef.current(state);在有条件的代码里做useEffect
2021-06-16 01:03:07

在 React16.x 及更高版本中,如果您想使用useState钩子在状态更改时调用回调函数,您可以使用useEffect附加到状态更改钩子。

import React, { useEffect } from "react";

useEffect(() => {
  props.getChildChange(name); // using camelCase for variable name is recommended.
}, [name]); // this will call getChildChange when ever name changes.
嗯超级。谢谢@DAMIENJIANG :)
2021-05-25 01:03:07
这也将在初始渲染时运行 props.getChildChange
2021-06-01 01:03:07
@Gucal 您可以多次使用 useEffect,例如 useEffect(() => loadFunctionAOnce()). useEffect(() => loadFunctionBIfNameChange(), [name])
2021-06-02 01:03:07
如果有多个函数,并且只有其中一个需要在重播中工作,我们该怎么办?
2021-06-16 01:03:07

实际上,this在使用 react hooks 时应该避免使用它会导致副作用。这就是为什么 react team create react hooks

如果删除代码的尝试绑定this,你可以简单地传递setNameParentChild,并调用它handleChange更干净的代码!

function Parent() {
  const [Name, setName] = useState("");

  return <div> {Name} :
    <Child setName={setName} ></Child>
  </div>
}

function Child(props) {
  const [Name, setName] = useState("");

  function handleChange(ele) {
    setName(ele.target.value);
    props.setName(ele.target.value);
  }

  return (<div>
    <input onChange={handleChange} value={Name}></input>
  </div>);
} 

此外,您不必创建两个副本Name(一个 inParent另一个 in Child)。坚持“Single Source of Truth”原则,Child不必拥有状态,Name而是从Parent. 更清洁的节点!

function Parent() {
  const [Name, setName] = useState("");

  return <div> {Name} :
    <Child setName={setName} Name={Name}></Child>
  </div>
}

function Child(props) {    
  function handleChange(ele) {
    props.setName(ele.target.value);
  }

  return (<div>
    <input onChange={handleChange} value={props.Name}></input>
  </div>);
} 

我们可以编写自定义函数,如果状态发生任何变化,它将调用回调函数

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const useStateCallbackWrapper = (initilValue, callBack) => {
  const [state, setState] = useState(initilValue);
  useEffect(() => callBack(state), [state]);
  return [state, setState];
};

const callBack = state => {
  console.log("---------------", state);
};
function App() {
  const [count, setCount] = useStateCallbackWrapper(0, callBack);
  return (
    <div className="App">
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

`

此解决方案在生产构建时失败 React Hook useEffect has a missing dependency: 'callBack'. Either include it or remove the dependency array. If 'callBack' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps
2021-06-10 01:03:07
尝试保持 useEffect 线像 useEffect(() => callBack?callBack(state):null, [state, callBack]);
2021-06-17 01:03:07