【问题标题】:React re-render not showing updated state array反应重新渲染不显示更新的状态数组
【发布时间】:2021-04-28 09:37:17
【问题描述】:

我正在事件处理函数中更新状态(向状态数组添加新对象)。

const handleIncomingData = (data) => {
  setState(state => {
    var _state = state
    if (_state.someDummyStateValue === 1234) {
      _state.arr.push({ message: data.message })
    }
    return _state
  })
}

React.useEffect(() => {
  socket.on('data', handleIncomingData)
  return()=>{
    socket.off('data', handleIncomingData)
  }
},[])

我正在以这种方式更新状态,因为事件处理函数无法访问最新的状态值。 console.log(state) 显示更新状态,但重新渲染后不显示新添加的对象。同样,当再次触发相同的事件时,会显示先前接收到的数据,但不会显示最新的数据。以这种方式更新状态有什么问题吗?

【问题讨论】:

    标签: javascript reactjs react-state


    【解决方案1】:

    javascript 中的对象是通过引用复制的(它存储在内存中的地址)。 当您执行以下操作时:

    let obj1 = {}
    let obj2 = obj1;
    

    obj2obj1 具有相同的引用。

    在您的情况下,您是直接复制状态对象。当您调用setState 时,它会触发重新渲染。如果前一个渲染的值与当前渲染的值相同,React 不会打扰更新。因此,您需要为您的状态创建一个新副本。

    试试这个:

    setState(prevValue => [...prevValue, {message: data.message}])
    

    为了更好地说明我的观点,这里有一个代码框:https://codesandbox.io/s/wild-snowflake-vm1o4?file=/src/App.js

    【讨论】:

    • 我以这种方式更新状态的原因是因为我正在对其他状态值进行某些条件检查,如果它们满足,那么我只想追加新数据。为此,我需要事件处理函数中不可用的最新状态值
    • 创建消息数组的新副本,因此,您可以执行以下操作。 var _state = [...state}@RahulJain
    • 看到您的沙箱代码后,我将 var _state = state 更改为 var _state = {...state} 并且它起作用了。你能解释一下可能是什么原因吗?
    • 我将更新答案以解释它发生的原因。同时,您能否将其标记为正确答案,以便对其他人有所帮助。 @RahulJain
    【解决方案2】:

    尝试使用默认的状态生成方式,这是首选、干净和简单的编码方式

    const [message, setMessage] = useState([])
    const handleIncomingData = data => {
       let newMessage = message;
        if (data.message.somevalue === 1234){ 
           newMessage.push(data.message);
           setMessage(newMessage)
        }
    }
    React.useEffect(() => {
      socket.on('data', handleIncomingData)
      return()=>{
        socket.off('data', handleIncomingData)
      }
    },[message])
    

    【讨论】:

    • 这样,你的状态中只有最后一条消息,我猜这不是目标
    • 条件检查不是数据而是状态值,为此我需要最新的状态值。
    【解决方案3】:

    是的,这是错误的...您应该永远不要直接在该州进行操作... 如果您想将消息附加到消息数组中,正确的 sn-p 代码将是:

    const [messages, setMessage] = useState([])
    const handleIncomingData = useCallback(
        data => {
            if (messages.someDummyStateValue === 1234) {
               setMessage([...messages,{message:data.message}])
            }
            // do nothing
    
    },[messages])
    
    React.useEffect(() => {
      socket.on('data', handleIncomingData)
      return()=>{
        socket.off('data', handleIncomingData)
      }
    },[handleIncomingData])
    

    这样我不会对任何状态进行任何操作,我只是用新状态替换旧状态(这只是带有新消息的旧状态)

    总是想在数组状态下操作数据,千万不要直接在状态下进行操作...

    【讨论】:

    • 我更新状态的原因是因为我正在对其他状态值进行某些条件检查,如果它们满足,那么我只想附加新数据。为此,我需要事件处理函数中不可用的最新状态值。
    • 没问题,您仍然可以通过这种方式使用您可能需要的任何验证,不是吗?可以举个例子吗?
    • 好的,我知道了,你只需要访问消息的实际值,做你的验证,如果你有一个有效的数据,追加它。
    • 这个解决方案的一个问题,我不会得到最新的状态值,因为这是事件处理函数,我正在使用功能组件。
    • 干得好。我认为你是对的,要解决它,你应该用钩子 useCallback 包装你的 handleIncomeData,并将“消息”状态添加到它的依赖项中......我更新了我的答案
    猜你喜欢
    • 2020-01-13
    • 2021-01-27
    • 1970-01-01
    • 2022-07-08
    • 1970-01-01
    • 1970-01-01
    • 2017-03-24
    • 2019-04-03
    • 1970-01-01
    相关资源
    最近更新 更多