【问题标题】:React: Updating state when state is an array of objectsReact:当状态是对象数组时更新状态
【发布时间】:2016-10-06 08:56:59
【问题描述】:

我有一组处于状态的对象:

this.state = {
  items: [
    {id: 1, someattr: "a string", anotherattr: ""},
    {id: 2, someattr: "another string", anotherattr: ""},
    {id: 3, someattr: "a string", anotherattr: ""},
  ]
}

我需要能够根据 id 属性搜索 items 数组,然后更新 objects 属性。

我可以使用 id 参数通过数组上的filteringfinding 获取对象。

我遇到的问题是更新数组,然后更新状态而不发生突变。

//make sure we're not mutating state directly by using object assign
const items = Object.assign({}, this.state.items);
const match = items.find((item) => item.id === id);

此时我有一个匹配的对象,可以使用对象扩展来更新它的属性:

const matchUpdated = { ...match, someattr: 'a new value'};

我的问题是如何使用matchUpdated 更新状态,以便覆盖初始查找操作返回的对象?

【问题讨论】:

  • 为什么不再使用 object.assign 呢?
  • 您能否举个例子,因为我不确定它是如何工作的?
  • const newState = Object.assign(this.state.items,matched) this.setState({items: newState}) ...如果我理解正确
  • 为什么不能直接使用this.state = { items: { 1: { someattr: ... }, 2: { ... }, ... } };this.state.items[id] = { ...this.state.items[id], someattr: 'a new value' }.

标签: reactjs ecmascript-6


【解决方案1】:

你的更新函数应该是这样的

updateItem(id, itemAttributes) {
  var index = this.state.items.findIndex(x=> x.id === id);
  if (index === -1)
    // handle error
  else
    this.setState({
      items: [
         ...this.state.items.slice(0,index),
         Object.assign({}, this.state.items[index], itemAttributes),
         ...this.state.items.slice(index+1)
      ]
    });
}

然后你就这样使用它

this.updateItem(2, {someattr: 'a new value'});

对吗?


如果您继续以这种方式构建复杂的应用程序,您通常会很头疼。我建议您查看 redux 或其他更适合解决这些问题的 Flux 实现。

Redux 使用状态缩减器的概念,每个缩减器都在应用程序状态的特定切片上工作。这样一来,您就不必在每次想要影响深度更改时手动挖掘整个状态。

Redux 的创建者 Dan Abramov 已免费在线提供两个视频课程。 Dan 是一位优秀的老师,我只花了一个下午就对 Redux 模式感到满意。

【讨论】:

  • 我自己使用 findIndex 提出了一个类似的解决方案!不能更同意使用 redux,我一直在使用它,但在这种情况下我不能使用它。
  • 如果数组长度在 50-80 左右,这意味着您在每次用户与 .name 文本框交互时都在使用 .find() ,这是一种非常糟糕的方法。
  • 我的意思是,我宁愿这个答案中的混乱而不是 redux
【解决方案2】:

如果你想使用一个函数,我会这样做......

const[array,setArray]= useState([
    {id: 1, value: "aws", othervalue: "was"},
    {id: 2, value: "goo", othervalue: "nano"},
    {id: 3, value: "micro", othervalue: "marcro"},
])

const updateItem =(id, whichvalue, newvalue)=> {
    let index = array.findIndex(x=> x.id === id); 
/*this line is only neccessay if your element's id 
isn't its postion/index in the array or related to it.
In the case that it is, use the id as the index, or run the function
(binary/hash) that relates the id to that position/index to find the index.
*/
    if (index !== -1){
        let temporaryarray = array.slice();
        temporaryarray[index][whichvalue] = newvalue;
        setArray(temporaryarray);
    }
    else {
        console.log('no match');
    }
}
    /* --longer version--
    var index = array.findIndex(x=> x.id === id);
    let g = array[index]
    g[whichvalue] = newvalue
    if (index === -1){
        console.log('no match')
    }
    else {
        setArray(
            [
            ...array.slice(0,index),
            g,
            ...array.slice(index+1)
            ]
        );
    }
    */
    
//how to use the function    
onPress={()=>updateItem(2,'value','John Lemon')}
onPress={()=>updateItem(1,'othervalue','Stringo Stra')}

函数的第一个输入是item的id。

函数的第二个输入是您希望更改的属性。

函数的第三个输入是该属性的新值。

【讨论】:

    【解决方案3】:

    如果你使用函数式组件和 useState 钩子,你可以很容易地使用 map,只要你不介意替换整个对象

    const [items, setItems] = useState ([
        {id: 1, someattr: "a string", anotherattr: ""},
        {id: 2, someattr: "another string", anotherattr: ""},
        {id: 3, someattr: "a string", anotherattr: ""},
    ])
    
    setItems (
        items.map((item) => {
            return item.id === updatedItem.id? updatedItem: item;
        })
    ); 
    

    【讨论】:

      【解决方案4】:

      添加到木兰的回答,你可以使用Object spread,它比Object.assign()更清晰易读

      updateItem(id, itemAttributes) {
        var index = this.state.items.findIndex(x=> x.id === id);
        if (index === -1)
          // handle error
        else
          this.setState({
            items: [
               ...this.state.items.slice(0,index),
              { ...this.state.items[index], itemAttributes },  
               ...this.state.items.slice(index+1)
            ]
          });
      }
      

      然后你就这样使用它

      this.updateItem(2, {someattr: 'a new value'});
      

      【讨论】:

        【解决方案5】:

        您可以遍历状态数组对象的值并替换状态。 在循环中,您可以决定要在哪条记录上应用新值(如果不是全部)。

        this.state = {
          items: [
            {id: 1, someattr: "a string", anotherattr: ""},
            {id: 2, someattr: "another string", anotherattr: ""},
            {id: 3, someattr: "a string", anotherattr: ""},
          ]
        }
        

        //回答

        const newState = Object.assign({}, this.state);
           
        newState.items.forEach(element => {
        
          element.someattr= "I like it a lot";
        
        });
        
        this.setState(newState);
        

        【讨论】:

          【解决方案6】:

          在这样的函数组件中 您必须将其添加到函数中

          const [item,setitem]=usestate([{id_1:null},{id_2:null}])
          ()=> setitems((prev) => { return { ...prev, id_1: "my change value"} }) 
          

          并像这样使用它

            console.log(items.id_1 == "my change value")
          

          【讨论】:

            猜你喜欢
            • 2021-12-26
            • 1970-01-01
            • 2020-10-21
            • 2020-05-26
            • 2020-07-01
            • 2021-01-14
            • 2019-06-13
            • 1970-01-01
            • 2021-01-27
            相关资源
            最近更新 更多