mik*_*gio 10 javascript reactjs react-hooks
我useState用来管理状态,它工作正常。但是当我在它的内部返回状态时,它总是初始值
import react, {useState} from 'react'
const MyComponent = () => {
const [myState, setMyState] = useState({
value: 'initial value',
setValue: (newValue) => {
setMyState({...myState, value: newValue})
console.log(myState.value) //<--always is 'initial value'
}
})
return(
<>
<p>{myState.value}</p> //<-- it's working fine
<input value={myState.value} onChange={(e) => myState.setValue(e.target.value)} /> //<--working fine too
</>
)
}
Run Code Online (Sandbox Code Playgroud)
我期望console.log输入的值,但实际输出总是初始值
got*_*top 22
const [myState, setMyState] = useState({
value: 'initial value',
setValue: (newValue) => {
setMyState({...myState, value: newValue})
console.log(myState.value) //<--always is 'initial value'
}
})
Run Code Online (Sandbox Code Playgroud)
The first time your component function is run, the setValue function captures the initial value of myState. The second time it is run, you make a copy of the setValue function—but this is the function that has has captured the initial value of myState. It never updates.
Since the function never changes, you should not put it in useState() in the first place. You can define the function separately.
const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = (newValue) => {
setMyState({ ...myState, value: newValue })
}
Run Code Online (Sandbox Code Playgroud)
Now, a new setValue copy is created every time the component function is run. When capturing variables, you can use useCallback() for optimization; if the values didn't change, React will reuse old copies of the function.
const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = useCallback((newValue) => {
setMyState({ ...myState, value: newValue })
}, [myState]) // ? this bit ensures that the value is always up to date inside the callback
Run Code Online (Sandbox Code Playgroud)
As mentioned by Shubham Khatri, there is an even faster and better approach in this situation: using the functional form of setState.
const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = useCallback((newValue) => {
setMyState((prev) => ({ ...prev, value: newValue }))
}, []) // ? now we use an empty array, so React will never update this callback
Run Code Online (Sandbox Code Playgroud)
Any of these three methods are fine to use, though; they will all work and perform good enough for most use cases.
Per comments, you're attempting to create an object that is passed down via context. A way to do this is by creating the context object in a separate step, similarly to how we created the callback function. This time, we use useMemo, which is similar to useCallback but works for any type of object.
// Per T.J. Crowder's answer, you can use separate `useState` calls if you need multiple values.
const [myState, setMyState] = useState('initial value')
const ctx = useMemo(() => ({
value: myState,
setValue: (newValue) => {
setMyState(newValue)
}
}), [myState])
return (
<Provider value={ctx}>
{children}
</Provider>
)
Run Code Online (Sandbox Code Playgroud)
goto-bus-stop 的答案解释了为什么您会遇到问题。但这里还有另一件事需要解决:
从您提供的代码来看,您似乎正在使用一个对象作为状态值。特别是这一行:
setMyState({...myState, value: newValue})
Run Code Online (Sandbox Code Playgroud)
..表明您打算myState在其中包含多种内容,而value只是其中之一。
这不是你使用 hook 的方式。将对象作为状态值很好,但通常当状态更改时要更新(即替换)整个对象时,您会这样做。如果您要更新状态的各个部分(如上面所建议的),您可以使用单独的useState调用而不是对象。看评论:
setMyState({...myState, value: newValue})
Run Code Online (Sandbox Code Playgroud)
const {useState} = React;
const MyComponent = () => {
// Separate `useState` calls for each state item
const [value, setValue] = useState('initial value');
const [anotherValue, setAnotherValue] = useState('another value');
// No need to create your own update function, that's what `setValue` and
// `setAnotherValue` are, just use them:
return(
<React.Fragment>
<p>{value}</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<p>{anotherValue}</p>
<input value={anotherValue} onChange={(e) => setAnotherValue(e.target.value)} />
</React.Fragment>
);
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)
如果状态更改有任何副作用,这种分离特别有用,因为您可以指定哪个状态触发副作用。例如,下面的组件在更改console.log时触发value,但在更改时不触发,以及当它们中的任何一个anotherValue更改时发生的另一种效果:
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>Run Code Online (Sandbox Code Playgroud)
const {useState, useEffect} = React;
const MyComponent = () => {
// Separate `useState` calls for each state item
const [value, setValue] = useState('initial value');
const [anotherValue, setAnotherValue] = useState('another value');
// A counter for all changes; we use -1 because
// our effect runs on initial mount
const [changes, setChanges] = useState(-1);
// No need to create your own update function, that's what `setValue` and
// `setAnotherValue` are, just use them:
// A side-effect for when `value` changes:
useEffect(() => {
console.log(`value changed to ${value}`);
}, [value]); // <=== Notice that we declare what triggers this effect
// A side-effect for when *either* `value` or `anotherValue` changes:
useEffect(() => {
setChanges(changes + 1);
}, [value, anotherValue]);
return(
<React.Fragment>
<p>Total changes: {changes}</p>
<p>{value}</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<p>{anotherValue}</p>
<input value={anotherValue} onChange={(e) => setAnotherValue(e.target.value)} />
</React.Fragment>
);
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
33923 次 |
| 最近记录: |