我正在使用带有挂钩的功能组件。我需要从孩子那里更新父母的状态,并且我正在使用父母的道具功能。 一切正常,除了我的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>);
}
答案 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)));