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

时间:2019-03-02 00:35:47

标签: reactjs

我正在使用带有挂钩的功能组件。我需要从孩子那里更新父母的状态,并且我正在使用父母的道具功能。 一切正常,除了我的prop函数获取最后一个状态而不是当前状态,因为在 useState 钩子设置当前状态之前执行我的prop函数。 我该如何等待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>);
} 

9 个答案:

答案 0 :(得分:25)

setState(updater, callback) for useState

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

Robin's solution的添加:

1)在初始渲染时省略了回调执行(我们只希望在状态更新上调用它)
2)对于每个setState调用,回调可以是动态的,就像使用类

用法

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

  const handleClick = () => {
    setState(
      prev => prev + 1,
      // 2nd argument is callback , `s` is *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); // mutable ref to store current callback

  const setStateCallback = (state, cb) => {
    cbRef.current = cb; // store passed callback to ref
    setState(state);
  };

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

  return [state, setStateCallback];
}

更多信息:React Hooks FAQ: Is there something like instance variables?

工作示例

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 = (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 } = React</script>
<div id="root"></div>

答案 1 :(得分:2)

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

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

`

答案 2 :(得分:2)

另一种实现方法:

const [Name, setName] = useState({val:"", callback: null});
React.useEffect(()=>{
  console.log(Name)
  const {callback} = Name;
  callback && callback();
}, [Name]);
setName({val:'foo', callback: ()=>setName({val: 'then bar'})})

答案 3 :(得分:2)

您可以利用useCallback钩子来做到这一点。

function Parent() {
  const [name, setName] = useState("");
  const getChildChange = useCallback( (updatedName) => {
    setName(updatedName);
  }, []);

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

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

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

  function collectState() {
    return name;
  }

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

答案 4 :(得分:1)

// Configure Jest using setupFilesAfterEnv
// to run a module that sets the window variables
// so they are already set before the test begins...

import { config } from './config';

test('setting window variables', () => {
  expect(config.param).toBe('foo');  // SUCCESS
  expect(config.url).toBe('bar');  // SUCCESS
});

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>); } 充当componentDidMount,componentDidUpdate,因此在更新状态后它将起作用

答案 5 :(得分:0)

实际上,在使用反应挂钩时,应避免使用this。它会引起副作用。这就是为什么反应小组创建react hooks

如果您删除尝试绑定this的代码,则只需将setName中的Parent传递到Child并在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中)。坚持“单一真相”原则,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>);
} 

答案 6 :(得分:0)

You can use useEffect/useLayoutEffect to achieve this:

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

If you are looking for an out of the box solution, check out this custom hook that works like useState but accepts as second parameter a callback function:

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

It can be installed via npm install use-state-with-callback

Note: If you want to make synchronous layout updates, use import { useStateWithCallbackInstant } from 'use-state-with-callback'; instead of useStateWithCallback.

答案 7 :(得分:0)

如果您想使用useState钩子来为状态更改提供回调函数,则可以使useEffect钩子依赖于该状态。

import React, { useState } from 'react';

useEffect(() => {
  props.getChildChange(Name); // using camelCase for variable name is recommended.
});

答案 8 :(得分:-5)

您可以按如下方式使用-

this.setState(() => ({  subChartType1: value   }), () => this.props.dispatch(setChartData(null)));