更新状态中的深层嵌套对象

时间:2018-08-16 07:24:47

标签: reactjs forms object immutability

我试图通过用户输入从表单中更新一些“对象”字段,并将它们设置为“状态”变量。我一直在寻找最好的方法来做到这一点,因为我的字段在整个对象中的结构都不同。我只想知道一种处理用户输入以动态更新状态对象的方法。     this.state = {       用户:{         userId:“”,

    email:{
      data:"",
      p_type: "2"
    },
    fName: {
      data:"",
      p_type: "2"
    },
    lName: {
      data:"",
      p_type: "2"
    },
    number: {
      data:"",
      p_type: "2"
    },

    address:{
      data:{
        street: "",
        suburb: "",
        postcode: ""
      },
      p_type: "1"
    }
  }

上面是我要更新的对象,我有一个onChangeHandler,如下所示。显然,以下方法不适用于address属性,但适用于其他方法。香港专业教育学院尝试了一些不同的事情,以使其工作,但没有一个是动态的,并且我不想创建多个onChangeHandlers,因为这只是对象的快照,并且会变得更大。

handleChange = event => {
  var updateUser=this.state.user;
  let field =event.target.id
  updateUser[field].data = event.target.value;
  this.setState({user: updateUser });
}

下面是“渲染”代码和用于更新状态对象的表单的代码

<form onSubmit={this.handleSubmit}>
  <FormGroup controlId="address.data.street" bsSize="large">
      <ControlLabel>Street</ControlLabel>
      <FormControl
          type="text"
          value={this.state.user.address.data.street}
          onChange={this.handleChange}
      />
  </FormGroup>
  <FormGroup controlId="address.data.suburb">
      <ControlLabel>Suburb</ControlLabel>
      <FormControl

          type="text"
          value={this.state.user.address.data.suburb}
          onChange={this.handleChange}
      />
  </FormGroup>

坦白地说,只要可以通过用户处理输入的方式是动态的并且可以与任何嵌套属性配合使用,就可以接受任何建议

2 个答案:

答案 0 :(得分:0)

您可以将JSONPath与onChange处理程序方法绑定使用。

https://github.com/json-path/JsonPath

在绑定中,您可以指定JSONPath,然后使用它来更新事件处理程序中的值。

希望这会有所帮助。编码愉快。

答案 1 :(得分:0)

重要的是要注意,由于状态是不可变的,因此不能简单地更新状态中的嵌套对象。在您的示例中执行updateUser[field].data = event.target.value;时,updateUser仍然是当前状态的用户-这意味着不允许这样做。

为此,我建议使用icepicklodash库,icepick旨在更新不可变对象(状态为)中的嵌套路径。 / p>

import * as i from "icepick";
import * as _ from "lodash";
...


getUserValue = (path) => _.get(this.state.user, path);

setUserValue = (path, value) => {
     const pathArr = _.toPath(path);
     const oldUser = this.state.user;
     const newUser = i.assocIn(oldUser, pathArr, value);
     i.freeze(newUser);
     this.setState({
         user: newUser,
     });
}

render() {
    return (
        <FormControl
            type="text"
            value={getUserValue("address.data.suburb")}
            onChange={e => setUserValue("address.data.suburb", e.target.value)}
        />
    );
}

现在这实际上可以立即使用,但是这里也存在性能问题-这是我们在render方法中有一个嵌套的arrow函数。每次反应渲染时,这将导致创建一个新功能,并且由于道具更改反应将重新生成dom。我们可以通过缓存功能来解决此问题。

具有功能缓存的新代码在这里:

import * as i from "icepick";
import * as _ from "lodash";
...


getUserValue = (path) => _.get(this.state.user, path);


setUserValueFnCache = {};
createSetUserValueFn = (path) => {
    if (!!setUserValueFnCache[path]) {
        return setUserValueFnCache[path];
    }

    const fn = (e) => {
        const pathArr = _.toPath(path);
        const oldUser = this.state.user;
        const newUser = i.assocIn(oldUser, pathArr, e.target.value);
        i.freeze(newUser);
        this.setState({
            user: newUser,
        });
    }

    setUserValueFnCache[path] = fn;
    return fn;
}

render() {
    return (
        <FormControl
            type="text"
            value={getUserValue("address.data.suburb")}
            onChange={createSetUserValueFn("address.data.suburb")}
        />
    );
}

现在您可以看到箭头函数已从我们的render方法中删除,但是更重要的是,由于我们已缓存了创建的函数,因此每次都会给同一个函数提供相同的功能。这意味着React将能够适当地优化我们对该组件的渲染。