如何在反应中使用带有useState钩子的回调[重复]
Posted
技术标签:
【中文标题】如何在反应中使用带有useState钩子的回调[重复]【英文标题】:How to use callback with useState hook in react [duplicate] 【发布时间】:2019-07-24 01:04:32 【问题描述】:我正在使用带有钩子的功能组件。我需要从孩子更新父母的状态。我在 Parent 中使用了一个 prop 函数。 一切正常,除了我的 prop 函数正在获取以前的状态而不是当前状态。我的 prop 函数在 useState 钩子设置当前状态之前执行。 我怎样才能等待我的回调函数在 useState 调用后执行。我正在从基于类的组件中寻找类似 setState(state,callback) 的东西。
这里是sn-p的代码:
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>);
【问题讨论】:
你为什么不直接把setName
传给孩子呢?
我希望我们在这个线程中得到有趣的 cmets github.com/facebook/react/issues/17969
有一个简单的方法可以做到这一点没有 useEffect ***.com/a/70405577/5823517
【参考方案1】:
对于 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.
【讨论】:
如果有多个函数,并且只有其中一个需要在replay中工作,我们该怎么办? @Gucal 你可以像useEffect(() => loadFunctionAOnce()). useEffect(() => loadFunctionBIfNameChange(), [name])
一样多次使用useEffect
嗯,超级棒。谢谢@DAMIENJIANG :)
这也会在初始渲染时运行 props.getChildChange【参考方案2】:
setState(updater, callback)
为useState
以下实现非常接近于类的原始setState
回调。
对accepted answer的改进:
-
在初始渲染时省略了回调执行 - 我们只想在状态 updates 时调用它
每个
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 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 = 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>
【讨论】:
cbRef.current(state);
在useEffect
的条件内的这段代码中做了什么?
@bot19 这是回调的实际调用,之前已通过setState(..,cb)
设置。 cbRef.current
存储一个函数。然后使用当前更新的状态调用此函数 - (state)
。
@dwjohnston bailing out of state updates 在相同值的情况下是 Hooks 的新 React 默认值 - 因此在大多数情况下我不会更改此行为。如果您出于遗留原因确实需要内联旧的基于类的比较(由于对象合并而以这种方式运行),那么代码和框方法看起来很合理!与其使用Symbol
,不如每次都将状态值包装在一个新的对象容器中。
@PetrÚjezdský 感谢您的想法!回复 1:我猜,this comment 很合适。 Re 2:如果您在 same 渲染周期和相同的钩子实例期间调用 setState
两次,则最后一个值在 React 中获胜。因此,如果同时调用旧的 和 新回调,我会在设置回调时期望相同的行为并且宁愿感到困惑。无论如何,这两种情况似乎都是一种边缘情况——很可能你会有一个事件处理程序,其中设置状态是在不同的渲染中完成的
考虑把它做成一个 npm 包!【参考方案3】:
您可以使用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>);
【讨论】:
这就是我要找的答案,谢谢! 在两个组件中为同一个变量设置状态对我来说听起来不是一个好主意。useState Hook doesn't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect()
。这就是我在回调后得到的结果
嘿@dishwasherWithProgrammingSkill,这段代码的用途是什么?像这样与 setState 内联有什么不同:<Child getChildChange=(value) => setValue(value) ></Child>
【参考方案4】:
实现此目的的另一种方法:
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'))
【讨论】:
这有点整洁。所以这样,执行顺序将取决于您如何设置键值?先val再回调?【参考方案5】:您可以使用 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>
);
;
更多信息请关注here。
如果您正在寻找开箱即用的解决方案,请查看 this custom hook,它的工作方式与 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 但它不起作用。它给出了一个错误。我能做什么? 一个非常好的方法。我发现它非常有用【参考方案6】:我们可以编写自定义函数,如果状态发生任何变化,它将调用回调函数
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
进行生产构建时失败
尽量保持 useEffect 行如useEffect(() => callBack?callBack(state):null, [state, callBack]);
【参考方案7】:
其实在使用 react hooks 的时候应该避免使用this
。它会引起副作用。这就是为什么反应团队创建react hooks
。
如果您删除试图绑定this
的代码,您只需将Parent
中的setName
传递给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>);
【讨论】:
【参考方案8】:你可以像下面这样使用 -
this.setState(() => ( subChartType1: value ), () => this.props.dispatch(setChartData(null)));
【讨论】:
OP 使用useState
钩子请求回调。不是setState
函数【参考方案9】:
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,所以在更新状态后会起作用
【讨论】:
以上是关于如何在反应中使用带有useState钩子的回调[重复]的主要内容,如果未能解决你的问题,请参考以下文章
带有回调的 React Hooks useState [重复]