函数仅在两次调用时更新状态

时间:2019-11-13 03:20:55

标签: javascript arrays reactjs react-native object

我有一个在单击时被调用以更新状态的函数,但是仅在单击两次时才更新。例如,如果单击一次,它将复制先前的更新,并且仅当再次单击时,状态才会更新。

    const select = day => {
        let markedDay = day.dateString
        setSelected([...selected, markedDay])

        let obj = selected.reduce((c, v) => Object.assign(c, {[v]: { selected: true,  disableTouchEvent: true }}), {})

        setBooking(obj)
    }

该函数的目的是:1)使用setSelected钩子将数据收集到数组中,以及2)使用Object.assignreduce将其转换为具有新分配属性的对象。

当函数被调用一次时,挂钩selected中的状态setSelected第一次显示为空或显示为先前状态。 obj使用相同的模式。我希望函数在第一次调用时更新状态,而不必被调用两次。

更新

我已将函数简化为以下函数,但仍然存在相同的问题:

    const select = day => {
        let markedDay = day.dateString
        setSelected({...selected, [markedDay]: { selected: true, disableTouchEvent: true  }})
    }

该功能适用​​于react-native-calendars,selected的示例为:

  "2019-11-18": Object {
    "selected": true,
    "disableTouchEvent": true,
  }

每次选择一个日期时,我都想查看上述对象在状态上的更新,但是只有当我第二次单击它时,它才会更新。

2 个答案:

答案 0 :(得分:1)

React中的设置状态是异步的,因此更新将不会反映到该状态,直到下一次渲染。代码的问题在于,设置状态后,您将立即使用状态的新值,但是更新后的值仅在下一个组件渲染时可用。要解决此问题,您可以在设置状态之前重用selected的更新值:

const select = day => {
  let markedDay = day.dateString
  const newSelected = [...selected, markedDay];

  let obj = newSelected.reduce((c, v) => Object.assign(c, {
    [v]: {
      selected: true,
      disableTouchEvent: true
    }
  }), {})
  setSelected(newSelected)
  setBooking(obj);
}

或者,如果您想在setBooking中使用更新后的状态值,则需要在跟踪useEffect变量的同时在selected内部进行该调用。

const select = day => {
  let markedDay = day.dateString

  setSelected([...selected, markedDay]);
}

useEffect(() => {
  let obj = selected.reduce((c, v) => Object.assign(c, {
    [v]: {
      selected: true,
      disableTouchEvent: true
    }
  }), {})

  setBooking(obj);
}, [selected])

当您具有更复杂的状态时,这可能是更可取的方法。

答案 1 :(得分:0)

我只能冒险猜测正在发生的事情,但是我认为我们在这里看到的是race condition。我猜测setBooking或setSelected应该从您的状态中获取值并调整最终产品的呈现,是吗?

问题在于react中的设置状态是异步的。如果我说

console.log(this.state.foo) // 'bar'
this.setState({foo:'fizz'})
console.log(this.state.foo) // still 'bar'

react仍在后台设置状态;还没有完成,所以我得到了旧值。

有三种方法可以修复此错误。

方法1:使渲染语句依赖于依赖状态的状态/调用函数。这样,只要状态更新,您呈现的内容就会更新。

方法2:将要存储在状态中的参数作为参数传递给setBooking或setSelected函数。如果他们只是接收值,则无需担心种族状况。

方法3:可以将强制setState写入为同步状态。

this.setState({foo:'bar'},()=>{ //do something }

此代码的“执行操作”部分直到设置了状态后才会执行。它有一点代码味道(毕竟,您没有利用asynch的优势),但是有时候,如果您想在componentDidMount中以及组件的生命周期中重用某个函数,则有必要。