【问题标题】:useState always is default value in itselfuseState 本身始终是默认值
【发布时间】:2019-05-13 09:01:34
【问题描述】:

我正在使用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

        </>
    )
}

我希望console.log 是输入值,但实际输出始终是初始值

【问题讨论】:

  • 该代码无法解析,这使得您很难理解您真正在做什么并帮助您理解问题所在。请使用 minimal reproducible example 来更新您的问题,以展示问题,最好是使用 Stack Snippets([&lt;&gt;] 工具栏按钮)的 runnable 问题。 Stack Snippets 支持 React,包括 JSX; here's how to do one。 (要使用挂钩,您需要按照那里的说明使用比下拉列表中更新的版本。)
  • useState 采用的唯一参数是初始状态。在我看来,您希望它采用某种配置对象。你在哪里找到的?正确的行更像是:const [myState, setState] = useState('initial value') 就是这样。
  • 请问你从哪里得到 usestate() 的语法?
  • 出于某种原因,我想在 MyState 中定义函数。
  • @mikenlanggio “出于某种原因”没有用。为了帮助您,我们需要知道为什么。 (在这种情况下,帮助您几乎肯定涉及解释要做什么,而不是在 MyState 中定义您的函数。)

标签: javascript reactjs react-hooks


【解决方案1】:
const [myState, setMyState] = useState({
    value: 'initial value',
    setValue: (newValue) => {
        setMyState({...myState, value: newValue})
        console.log(myState.value) //<--always is 'initial value'
    }
})

您的组件函数第一次运行时,setValue 函数会捕获myStateinitial 值。第二次运行时,您复制了setValue 函数——但这个函数已经捕获了myStateinitial 值。它永远不会更新。

因为函数永远不会改变,所以你不应该把它放在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

不过,这三种方法中的任何一种都可以使用;对于大多数用例,它们都可以正常工作并表现得足够好。


对于每个 cmets,您正在尝试创建一个通过上下文传递的对象。一种方法是在单独的步骤中创建上下文对象,类似于我们创建回调函数的方式。这一次,我们使用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>
)

【讨论】:

  • 我知道!但是,出于某种原因,我想在 MyState 中定义函数。
  • 是什么原因?您很可能需要做一些不同的事情。
  • 但是,在pinput 内部。它仍然工作正常。对吧?
  • 是的。我把它当作 useContext 的提供者来使用。我想在子组件中更改状态本身
  • 为什么还要将对象传递给useState?只需传递字符串本身,useState 返回的函数甚至会按照您的需要更新它,而无需提供自定义处理程序。
【解决方案2】:

goto-bus-stop's answer 解释了您遇到问题的原因。但这里还有一件事要解决:

从您提供的代码来看,您似乎正在使用一个对象作为您的状态值。特别是这一行:

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

..建议您打算 myState 包含多个内容,value 只是其中之一。

这不是你用钩子做的。将对象作为状态值很好,但通常您会在状态更改时更新(即替换)整个 对象时这样做。如果您要更新状态的各个部分(如上述建议),则使用单独的 useState 调用而不是对象。见 cmets:

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>

如果您有任何状态更改的副作用,这种分离特别有用,因为您可以指定什么状态触发副作用。例如,上面的组件会在value 更改时触发console.log,但在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>

【讨论】:

    【解决方案3】:

    更好的方法是

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

    【讨论】:

    • 我知道。但是,出于某种原因,我必须在 MyState 中定义函数。
    • 不推荐。 state 通常用于变量而不是函数
    【解决方案4】:

    首先,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)} />
    
            </>
        )
    }
    

    状态更新也是异步的,所以更新不会在下一次渲染后立即反映

    【讨论】:

    • prev 工作正常,但是如何在不调用 setMyState 的情况下控制当前的 myState?
    猜你喜欢
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 2020-05-19
    • 2016-06-29
    • 2021-01-19
    • 1970-01-01
    • 2014-11-10
    • 1970-01-01
    相关资源
    最近更新 更多