简体   繁体   English

useState 本身始终是默认值

[英]useState always is default value in itself

I'm using useState to manage the state , it's working fine.我正在使用useState来管理state ,它工作正常。 But when i return state inside itseft, it always value of initial但是当我在它的内部返回状态时,它总是初始值

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

        </>
    )
}

I expect the console.log is value of input, but the actual output always is initial value我希望console.log是输入值,但实际输出始终是初始值

goto-bus-stop's answer explains why you're getting the problem you're getting. goto-bus-stop的答案解释了为什么您遇到了问题。 But there's another thing to address here: 但是这里还有另一件事要解决:

From the code you've provided, it looks like you're using an object as your state value. 从您提供的代码来看,您似乎正在使用一个对象作为状态值。 In particular, this line: 特别是这一行:

setMyState({...myState, value: newValue})

..suggests you intend myState to have multiple things in it, with value just being one of them. ..建议您打算让myState包含多个内容,而value只是其中之一。

That's not how you do it with hooks. 那不是你用钩子做的方式。 It's fine to have an object as a state value, but generally you do that when you're going to update (that is, replace) the entire object when the state changes. 将对象作为状态值很好,但是通常在状态更改时要更新(即替换) 整个对象时,可以这样做。 If you're updating individual parts of the state (as suggested by the above), you use individual useState calls instead of an object. 如果要更新状态的各个部分(如上文所述),请使用单个useState调用而不是对象。 See comments: 看评论:

 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> 

This separation is particularly useful if you have any side effects of state changes, because you can specify what state triggers the side effect. 如果您有状态更改的任何副作用,则这种分隔特别有用,因为您可以指定触发状态的状态。 For instance, here's the component above which triggers a console.log when value changes but not when anotherValue changes, and another effect that occurs when either of them changes: 举例来说,这里是上面这将触发组件console.logvalue的变化,但不是当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> 

const [myState, setMyState] = useState({
    value: 'initial value',
    setValue: (newValue) => {
        setMyState({...myState, value: newValue})
        console.log(myState.value) //<--always is 'initial value'
    }
})

The first time your component function is run, the setValue function captures the initial value of myState . 第一次运行组件函数时, setValue函数将捕获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 . 第二次运行时,您将复制setValue函数,但这是已捕获myState 初始值的myState It never updates. 它永远不会更新。

Since the function never changes, you should not put it in useState() in the first place. 由于该函数永远不会更改,因此您不应首先将其放在useState()中。 You can define the function separately. 您可以单独定义函数。

const [myState, setMyState] = useState({ value: 'initial value' })
const setValue = (newValue) => {
  setMyState({ ...myState, value: newValue })
}

Now, a new setValue copy is created every time the component function is run. 现在,每次运行组件函数时都会创建一个新的setValue副本。 When capturing variables, you can use useCallback() for optimization; 捕获变量时,可以使用useCallback()进行优化; if the values didn't change, React will reuse old copies of the function. 如果值没有改变,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

As mentioned by Shubham Khatri, there is an even faster and better approach in this situation: using the functional form of setState . 正如Shubham Khatri所提到的,在这种情况下,有一个更快,更好的方法:使用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

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. 这次,我们使用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>
)

Firstly the function inside the useState argument isn't aware of the update since its only called once and has the value from its closure. 首先,useState参数内部的函数不知道更新,因为该更新仅被调用一次,并且具有来自其关闭的值。 Secondly the way you are using useState is not correct, you must instead only have the value in useState and have the handlers outside 其次,您使用useState的方式不正确,您必须只在useState中使用值,并在外部使用处理程序

Also you must use callback pattern 另外你必须使用回调模式

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

        </>
    )
}

Also state update is asynchronous so the update is not reflected immediately instead after the next render 而且状态更新是异步的,因此更新不会立即反映,而是在下一次渲染之后立即反映

A better way to do this will be 一个更好的方法是

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

Using setTimeout should work fine in your case: 在您的情况下,使用setTimeout应该可以正常工作:

setTimeout(()=>{
  console.log(myState.value)
}, 0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM