如何使用回调与useState挂钩在反应

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

下面是代码片段:

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>);
}
362803 次浏览

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钩子时,应该避免使用this。它会产生副作用。这就是react团队创建react hooks的原因。

如果你删除了试图绑定this的代码,你可以简单地将ParentsetName传递给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>);
}

你可以使用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.

我们可以编写自定义函数,它将在状态发生任何变化时调用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);

另一种实现方法:

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

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

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];
}
打印稿版本
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>