【问题标题】:Update data in form. Stay editable. (React with Redux, sans redux-form)更新表格中的数据。保持可编辑。 (React with Redux, sans redux-form)
【发布时间】:2017-04-25 21:59:56
【问题描述】:

问题

我有一份人员名单。我想:

  1. 点击用户名选择要编辑的用户。
  2. 编辑该用户的信息,以便我可以单击提交按钮并更新列表。
  3. 如果我单击其他姓名,我想切换到该人的信息,而不必先故意关闭表单。

在 #3 之前一切正常。当我点击另一个人时,表单本身不会更新。

我的代码

更新组件用于更新表单:

const UpdateForm = ({ updatePerson, personToUpdate, handleInputChange }) => {

  let _name, _city, _age, _id;

  const submit = (e) => {
    e.preventDefault();

    updatePerson({
      name: _name.value,
      city: _city.value,
      age: _age.value,
      _id: _id.value
    });

  };

  return (
    <div>
      <form onSubmit={submit}>
        <h3>Update Person</h3>
        <label htmlFor="_id">Some Unique ID: </label>
        <input type="text" name="_id" ref={input => _id = input}  id="_id" defaultValue={personToUpdate._id} onChange={input => handleInputChange(personToUpdate)} required />
        <br />
        <label htmlFor="name">Name: </label>
        <input type="text" name="name" ref={input => _name = input} id="name" defaultValue={personToUpdate.name} onChange={input => handleInputChange(personToUpdate)} />
        <br />
        <label htmlFor="city">City: </label>
        <input type="text" name="city" ref={input => _city = input} id="city" defaultValue={personToUpdate.city} onChange={input => handleInputChange(personToUpdate)} />
        <br />
        <label htmlFor="age">Age: </label>
        <input type="text" name="age" ref={input => _age = input} id="age" defaultValue={personToUpdate.age} onChange={input => handleInputChange(personToUpdate)} />
        <br />
        <input type="submit" value="Submit" />
      </form>

    </div>
  );
};

export default UpdateForm;

Person 组件的相关部分

class Person extends Component {

  nameClick() {
    if (this.props.person._id !== this.props.personToUpdate._id) {
      this.props.setForUpdate(this.props.person);
      this.forceUpdate();
    }
    else {
      this.props.toggleUpdatePersonPanel();
    }

  }

    render() {
        return (
          <div>
            <span onClick={this.nameClick}>
                {this.props.person.name} ({this.props.person.age})
            </span>
          </div>
        );
    }
}

export default Person;

PeopleList 的相关部分,其中包含 Persons:

class PeopleList extends Component {

  render() {
    return(
      <div>
        {this.props.people.map((person) => {
          return <Person
            key={person._id}
            person={person}
            updatePersonPanel={this.props.updatePersonPanel}
            setForUpdate={this.props.setForUpdate}
            personToUpdate={this.props.personToUpdate}
            />;
        })}
      </div>
    );
  }

} // end class

export default PeopleList;

Form Reducer,仅包含相关操作:

export default function formReducer(state = initialState.form, action) {
  let filteredPeople;

  switch (action.type) {
    case TOGGLE_UPDATE_PANEL:
      return Object.assign({}, state, { updatePersonPanel: false }, { personToUpdate: {
        _id: "",
        name: "",
        city: "",
        age: ""
      }});
    case SET_FOR_UPDATE:            
      return Object.assign({}, state, { personToUpdate: action.person }, { updatePersonPanel: true });
    case UPDATE_RECORD:
      filteredPeople = state.people.filter((person) => {
        return person._id === action.person._id ? false : true;
      }); // end filter

      return Object.assign({}, state, { people: [ ...filteredPeople, action.person] }, { personToUpdate: {
        _id: "",
        name: "",
        city: "",
        age: ""
      }}, { updatePersonPanel: false });
    case HANDLE_INPUT_CHANGE:
      return Object.assign({}, state, { personToUpdate: action.person });
    default:
      return state;
  }

}

我的初始状态文件的相关部分:

  form: {
    people: [
      {
        _id: "adfpnu64",
        name: "Johnny",
        city: "Bobville",
        age: 22
      },
      {
        _id: "adf2pnu6",
        name: "Renee",
        city: "Juro",
        age: 21
      },
      {
        _id: "ad3fpnu",
        name: "Lipstasch",
        city: "Bobville",
        age: 45
      }
    ],
    updatePersonPanel: false,
    personToUpdate: {
      _id: "",
      name: "",
      city: "",
      age: ""
    },
  }

解决方案的尝试(到目前为止)

  • 我试图通过将表单属性切换为value 而不是defaultValue,使该组件成为一个完全受控的组件。当我这样做时,名称切换得很好,但形式变得不可更改且无用。

我的问题

几乎所有此类问题的解决方案都推荐使用 redux-form 或提供 two-way binding solutions that work fine in React without reduce我想知道如何在不使用 redux-form 或其他任何可能的情况下使用 Redux 来做到这一点。有没有办法在不触及生命周期方法的情况下解决这个问题?

结论(目前)

好吧,现在,我决定让我的表单不受控制,并使用一些经典的 Js DOM 方法和生命周期方法来控制表单。无论出于何种原因,一旦我使用了一些答案建议,我的浏览器就会耗尽我的 CPU 并崩溃,大概是因为存在某种无限循环。如果有人有进一步的建议,我将不胜感激。现在我满足于这个:

class UpdateForm extends Component {

  constructor(props){
    super(props);

    this.submit = this.submit.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.personToUpdate._id !== this.props.personToUpdate._id) {
      document.getElementById("name").value = nextProps.personToUpdate.name;
      document.getElementById("age").value = nextProps.personToUpdate.age;
      document.getElementById("city").value = nextProps.personToUpdate.city;
    }

  }

  submit(e) {
    e.preventDefault();

    this.props.updatePerson({
      name: document.getElementById("name").value,
      city: document.getElementById("city").value,
      age: document.getElementById("age").value,
      _id: this.props.personToUpdate._id
    });
  }

  render() {
    return (
      <div>
        <form onSubmit={this.submit}>
          <h3>Update Person</h3>
          Unique ID: {this.props.personToUpdate._id}
          <br />
          <label htmlFor="name">Name: </label>
          <input type="text" name="name" id="name" ref="name" defaultValue={this.props.personToUpdate.name} required />
          <br />
          <label htmlFor="city">City: </label>
          <input type="text" name="city" id="city" ref="city" defaultValue={this.props.personToUpdate.city} required />
          <br />
          <label htmlFor="age">Age: </label>
          <input type="text" name="age" id="age" ref="age" defaultValue={this.props.personToUpdate.age} required />
          <br />
          <input type="submit" value="Update" />
        </form>

      </div>
    );
  }
} // end class

export default UpdateForm;

我将很快探索redux-form,因为很明显,作为输入和输出的表单是一项不稳定的业务。目前,我的小应用程序可以运行。

【问题讨论】:

    标签: javascript forms reactjs redux react-redux


    【解决方案1】:

    是的,你在正确的道路上。方法是使用value而不是defaultValue,但是你必须从状态中读取value,然后使用onChange处理程序来修改状态。

    类似

    this.state = {inputText:''}
    

    然后在输入字段中

    <input value={this.state.inputText} onChange={this.handleChange}/>
    

    handleChange 函数将是

    handleChange(event){
       this.setState({inputText:event.target.value})
    }
    

    记得在构造函数中绑定handleChange 事件,这样你就可以在输入字段的onChange 属性中将它作为this.handleChange 传递。

    类似的东西

    this.handleChange = this.handleChange.bind(this)
    

    https://facebook.github.io/react/docs/forms.html - 这是有关它的官方文档

    此外,如果您想在 redux 中执行此操作,则同样的逻辑适用于输入字段

    <input value={this.props.inputText} onChange={this.props.handleChange}/>
    

    其中inputTexthandleChange分别是通过props传递给组件的redux状态和动作

    对于您的情况,我猜它必须类似于您从 people 数组中“读取”值的位置,并且绑定到 onChangeaction 修改了 @ 中 people 数组中的该值987654343@.

    如何针对该案例进行操作。将 redux 状态下的 people 作为 people 属性传递给组件。将操作 changePeople(newPeople) 作为 prop 传递给组件,该组件接受参数 newPeople 并将 redux 状态下的 people 更改为具有值 newPeople。由于people 嵌套在form 中,因此您必须执行一些Object.assign 等操作来修改状态。

    现在在使用people 属性的组件中,使用map 函数填充复选框。 map 函数接受第二个参数 index,因此每个复选框都有一个函数将本地状态变量 currentPerson 设置为 index 的值

    this.props.people.map((person,index) => 
        return <Checkbox onClick={()=>this.setState(currentPerson:index)}/>
    ) 
    

    因此,每次您单击复选框时,currentPerson 都会指向相应的 index

    现在输入字段可以是

    <input value={this.props.people[this.state.currentPerson].name} onChange={this.handleChange.bind(this,'name')}/>
    

    这是用于“名称”输入字段。它从people 数组的currentPerson 索引中读取,该数组已作为prop 向下传递。

    这就是handleChange 的样子

    handleChange(property,event){
      const newPeople = [
                        ...this.props.people.slice(0, this.state.currentPerson),
                        Object.assign({}, this.props.people[this.state.currentPerson], {
                            [property]: event.target.value
                        }),
                        ...this.props.people.slice(this.state.currentPerson + 1)
                        ]
    
      this.props.changePeople(newPeople)
    
    }
    

    handleChange 带有一个属性(因此您不必为每个输入字段编写单独的处理程序)。 newPeople 基本上修改了从props 传递的people 中当前索引this.state.currentPerson 处的元素(这里使用的是ES6 语法。如果您有疑问,请询问)。然后使用同样作为道具传递的changePeople 动作来调度它。

    【讨论】:

    • 感觉就像我以前尝试过的一样,但我会接受这些建议并真正确定。我会尽快报告。
    • 你知道吗... this.props.inputText 可以在对象上,例如 this.props.inputTextObject.nameInput 还是太深而无法对差异做出反应?
    • 是的,可以。由于people 在您创建复选框时是一个数组,因此可能会从人员数组中映射它,以便单击每个复选框时使currentUser(组件状态的一部分)等于数组的index。然后它可能类似于&lt;input value={this.props.people[index].name} onChange={this.handleChange.bind(this,'name')}/&gt; 所以现在随着索引的变化,输入中的值不断变化。并且handleChange 可以调度操作并在redux state 中保持更改原始people。我将添加原始答案。
    • 进步!我今天学到的如何应用 Function.prototype.bind 值得 100 票。我让它工作了,但是......如果我真的让它成为一个完全受控的组件并为每次更改更新我的状态值,我会收到一个范围错误。它在无限循环中使浏览器窗口崩溃。
    • 我给出的代码示例会更新每次击键时的状态,我认为这不是您想要的。为什么不将所有更改存储在本地 state 变量中(在组件中)并仅在按下提交时调度。如果有取消按钮,则使本地 state 等于 redux state。这样,您将大大减少 redux 中的调度数量。但即便如此,也不应该出现范围错误。
    猜你喜欢
    • 2019-04-04
    • 1970-01-01
    • 2016-08-10
    • 1970-01-01
    • 2020-09-23
    • 2020-01-16
    • 1970-01-01
    • 2017-06-20
    • 2018-09-19
    相关资源
    最近更新 更多