0

我有一些值存储在本地存储中。当我的组件挂载时,我想将这些值加载到状态中。但是,只有最后添加的属性会添加到状态中。我检查了我的 localStorage 上的值,它们都在那里。此外,当我在条件块中记录变量(desc、pic 或 foo)时,它们就在那里。

起初我认为每个后续 if 块都在重写状态,但事实并非如此,因为我正确使用了扩展运算符(我认为!),在所有预先存在的属性之后添加新属性。

我认为问题在于最后一个 if 块中的代码在第一个 if 块中设置状态之前运行。如何编写代码,以便将本地存储中的所有三个属性都放入状态?

//what I expect state to be
{
  textArea: {
    desc: 'some desc',
    pic: 'some pic',
    foo: 'some foo'
  }
}

//what the state is
{
  textArea: {
    foo: 'some foo'
  }
}

  componentDidMount () {

    const desc = window.localStorage.getItem('desc');
    const pic = window.localStorage.getItem('pic');
    const foo = window.localStorage.getItem('foo');

    if (desc) {
      console.log(desc) //'some desc'
      this.setState({
        ...this.state,
        textArea: {
          ...this.state.textArea,
          desc: desc,
        },
      }, ()=>console.log(this.state.textArea.desc)); //undefined
    }

    if (pic) {
      console.log(pic) //'some pic'
      this.setState({
        ...this.state,
        textArea: {
          ...this.state.textArea,
          pic: pic,
        },
      }, ()=>console.log(this.state.textArea.pic)); //undefined
    } 

    if (foo) {
      console.log(foo) //'some foo'
      this.setState({
        ...this.state,
        textArea: {
          ...this.state.textArea,
          foo: foo,
        },
      }, ()=>console.log(this.state.textArea.foo)); //'some foo'
    } 
  }

4

1 回答 1

2

你很可能被 React 批处理 setState 通过浅合并你传递的参数所捕获。这将导致仅应用最后一次更新。您可以通过仅调用 setState 一次来解决此问题,例如:

  componentDidMount () {

    const desc = window.localStorage.getItem('desc');
    const pic = window.localStorage.getItem('pic');
    const foo = window.localStorage.getItem('foo');

    this.setState({
        textArea: Object.assign({},
            desc ? { desc } : {},
            pic ? { pic } : {},
            foo ? { foo } : {}
        )
    });
  }

另一个版本是将更新函数传递给 setState 而不是更新对象,这样可以安全地在多个调用中使用。该函数传递了两个参数:先前的状态和当前的道具——无论您从函数返回什么,都将被设置为新状态。

  componentDidMount () {

    const desc = window.localStorage.getItem('desc');
    const pic = window.localStorage.getItem('pic');
    const foo = window.localStorage.getItem('foo');

    this.setState(prevState => {
        if (desc) {
            return {
                textArea: {
                   ...prevState.textArea,
                   desc
                }
            }
        } else {
            return prevState;
        }
    });
    // Repeat for other properties
  }

使用这种方法有点冗长,但确实提供了在组件外部提取状态更新函数以实现可测试性的机会:

  // Outside component
  const updateSubProperty = (propertyName, spec) => prevState => {
      return {
          [propertyName]: {
             ...prevState[propertyName],
             ...spec
          }
      }
  }

  const filterNullProperties = obj => {
      return Object.keys(obj).reduce((out, curr) => {
          return obj[curr] ? { ...out, [curr]: obj[curr] } : out;
      }, {});
  }

  componentDidMount () {
      this.setState(updateSubProperty("textArea",
          filterNullProperties(
              desc: window.localStorage.getItem('desc'),
              pic: window.localStorage.getItem('pic'),
              foo: window.localStorage.getItem('foo')
          )
      ));
  }

这种方式增加了一些复杂性,但(在我看来)提供了一个真正可读的组件,我们未来的自己清楚我们想要实现的目标。

于 2017-12-03T22:53:10.453 回答