我正在使用useState
来管理状态,它工作正常。但是,当我在iteft内返回状态时,它始终是初始值
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
</>
)
}
我希望console.log
是输入值,但实际输出始终是初始值
答案 0 :(得分:3)
goto-bus-stop's answer解释了为什么遇到问题的原因。但是这里还有另外一件事要解决:
从您提供的代码来看,您似乎正在使用一个对象作为状态值。特别是这一行:
setMyState({...myState, value: newValue})
..建议您打算myState
包含多个内容,而value
只是其中之一。
那不是用钩子做的方式。将对象作为状态值是可以的,但是通常在状态更改时要更新(即替换) entire 对象时,可以这样做。如果要更新状态的各个部分(如上所建议),请使用单个useState
调用而不是对象。查看评论:
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"));
<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>
如果状态更改有任何副作用,则这种分隔特别有用,因为您可以指定触发状态的状态。例如,这是上面的组件,当console.log
发生变化时,它会触发value
,而anotherValue
发生变化时,它会触发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"));
;当它们 发生变化时,也会触发另一种效果:
<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>
doctest
答案 1 :(得分:1)
const [myState, setMyState] = useState({
value: 'initial value',
setValue: (newValue) => {
setMyState({...myState, value: newValue})
console.log(myState.value) //<--always is 'initial value'
}
})
第一次运行组件函数时,setValue
函数将捕获myState
的 initial 值。第二次运行时,将复制setValue
函数-但这是已捕获myState
的 initial 值的函数。它永远不会更新。
由于函数永不更改,因此您不应首先将其放在useState()
中。您可以单独定义函数。
const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = (newValue) => {
setMyState({ ...myState, value: newValue })
}
现在,每次运行组件功能时,都会创建一个新的setValue
副本。捕获变量时,可以使用useCallback()
进行优化;如果值没有改变,React将重用该函数的旧副本。
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
如Shubham Khatri所提到的,在这种情况下,有一个更快,更好的方法:使用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
不过,这三种方法中的任何一种都可以使用。在大多数情况下,它们都能正常工作并表现良好。
对于每个注释,您试图创建一个通过上下文传递的对象。一种方法是在单独的步骤中创建上下文对象,类似于我们创建回调函数的方式。这次,我们使用useMemo
,它与useCallback
类似,但适用于任何类型的对象。
// 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>
)
答案 2 :(得分:0)
首先,useState参数内部的函数不知道更新,因为该更新仅被调用一次,并且具有其关闭的值。其次,您使用useState的方式不正确,您必须只在useState中使用值,并在外部使用处理程序
还必须使用回调模式
import react, {useState} from 'react'
const MyComponent = () => {
const [myState, setMyState] = useState('initial value');
const setValue = (newValue) => {
setMyState(newValue)
}
console.log(myState);
return(
<>
<p>{myState}</p>
<input value={myState} onChange={(e) => setValue(e.target.value)} />
</>
)
}
状态更新也是异步的,因此更新不会立即反映出来,而是在下一次渲染之后反映出来
答案 3 :(得分:0)
一种更好的方法是
import react, {useState} from 'react'
const MyComponent = () => {
const [ value, setValue ] = useState('initial value');
const handleChange = (e) => {
setValue(e.target.value);
}
return(
<>
<p>{myState}</p>
<input value={myState.value} onChange={handleChange} />
</>
)
}
答案 4 :(得分:-1)
在您的情况下,使用setTimeout
应该可以正常工作:
setTimeout(()=>{
console.log(myState.value)
}, 0)