我正在使用带有钩子的功能组件。我需要更新状态在父母从一个孩子。我使用的道具功能在父母。 一切都很好,除了我的道具函数是得到以前的状态,而不是当前的状态。我的道具函数在useState钩子设置当前状态之前执行。 我怎么能在useState调用后等待回调函数执行。我正在寻找类似setState(状态,回调)从基于类的组件。

下面是代码片段:

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

当前回答

React16。如果你想使用useState钩子在状态改变时调用一个回调函数,你可以使用useEffect钩子附加到状态改变。

import React, { useEffect } from "react";

useEffect(() => {
  props.getChildChange(name); // using camelCase for functions is recommended.
}, [name]); // this will call getChildChange on initial render and when ever name changes.

其他回答

setState(updater, callback) for useState

接下来的实现非常接近类的原始setState回调。

对已接受答案的改进:

回调执行在初始渲染时被省略了——我们只希望在状态更新时调用它 对于每个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];
}
TypeScript version
function useStateCallback<T>(
  initialState: T
): [T, (state: T, cb?: (state: T) => void) => void] {
  const [state, setState] = useState(initialState);
  const cbRef = useRef<((state: T) => void) | undefined>(undefined); // init mutable ref container for callbacks

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

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

  return [state, setStateCallback];
}

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

工作示例

const App = () => { const [state, setState] = useStateCallback(0); const handleClick = () => setState( prev => prev + 1, // important: use `s`, not the stale/old closure value `state` s => console.log("I am called after setState, state:", s) ); return ( <div> <p>Hello Comp. State: {state} </p> <button onClick={handleClick}>Click me</button> </div> ); } function useStateCallback(initialState) { const [state, setState] = useState(initialState); const cbRef = useRef(null); const setStateCallback = useCallback((state, cb) => { cbRef.current = cb; setState(state); }, []); useEffect(() => { if (cbRef.current) { cbRef.current(state); cbRef.current = null; } }, [state]); return [state, setStateCallback]; } ReactDOM.render(<App />, document.getElementById("root")); <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <script>var { useReducer, useEffect, useState, useRef, useCallback } = React</script> <div id="root"></div>

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); collectState = collectState.bind(this); function handleChange(ele) { setName(ele.target.value); } function collectState() { return Name; } useEffect(() => { props.getChildChange(collectState()); }); return (<div> <input onChange={handleChange} value={Name}></input> </div>); }

useEffect作为componentDidMount, componentDidUpdate,所以更新状态后它将工作

实际上,你应该避免在使用react钩子时使用这个。它会产生副作用。这就是react团队创建react钩子的原因。

如果你删除了试图绑定它的代码,你可以简单地将父的setName传递给子,并在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的两个副本(一个在Parent中,另一个在Child中)。坚持“单一真相来源”原则,孩子不必拥有国家名称,而是从父母那里接受。清洁节点!

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

你可以使用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>
  );
};

如果你想防止回调在第一次渲染时运行,请调整之前的版本:

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

  const didMount = React.useRef(false);

  React.useEffect(() => {
    if (!didMount.current) {
      didMount.current = true;
      return;
    }

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

这里有更多关于它的信息。

React16。如果你想使用useState钩子在状态改变时调用一个回调函数,你可以使用useEffect钩子附加到状态改变。

import React, { useEffect } from "react";

useEffect(() => {
  props.getChildChange(name); // using camelCase for functions is recommended.
}, [name]); // this will call getChildChange on initial render and when ever name changes.