【问题标题】:How do I get my components to return a serialization of themselves?如何让我的组件返回自己的序列化?
【发布时间】:2016-02-05 21:07:59
【问题描述】:

我还在学习 React 和 Javascript,所以感谢您的耐心等待。

我正在尝试序列化我的表单数据,以便将其发送到 Ruby on Rails 后端进行处理。我只是在使用 vanilla React,没有额外的依赖,如 Flux、Redux 等。

似乎我的子组件没有返回任何内容,我不太清楚为什么。

我试过了:

  • 通过使用 refs 公开值(但失败并读到这样做并不是一个好主意)
  • 在我的子组件中公开父方法以收集有关每个单独组件的信息(您将在我的 jsfiddle 中看到)。
  • 通过 onChange 方法更新组件状态并尝试访问每个子组件的状态

我的 JSFiddle:https://jsfiddle.net/morahood/0ptdpfu7/91/

我显然在这里遗漏了 React 设计模式的一个关键元素。我只是偏离轨道了吗?我怎样才能回到正轨?我希望最终能够以以下格式序列化表单数据

{
   "service_request" : {
      "services" : [
        {
          "service_item" : ["Oil Change", "New Windshield Wipers"],
          "customer" : "Troy",
          "manufacturer" : "Ford",
          "model" : "F150"
        },
        {
          "service_item" : ["Tire Rotation"],
          "customer" : "Dave",
          "manufacturer" : "Hyundai",
          "model" : "Genesis"
        }
      ]
    }
}

组件

var ServiceForm = React.createClass({
  render: function() {
    return (
      <form onSubmit={ this.handleFormSubmission }>
        { this.state.serviceItems.map(function(item) {
          return (item);
        })}
        <div className="btn btn-default btn-sm" onClick={ this.addServiceItem }>+ Append New Service Item</div>
        <button type="submit" className="btn btn-success btn-lg pull-right">Submit</button>
      </form>
    );
  },

  getInitialState: function() {
    return ({
      serviceItems: [<ServiceItem serializeServiceItem={ this.serializeServiceItem } />]
    });
  },

  handleFormSubmission: function() {
    console.log("form submitted!");
    alert("Serialized Form Data: " +  this.serializeFormData());
  },

  addServiceItem: function(event) {
    var serviceItems = this.state.serviceItems;
    serviceItems.push(<ServiceItem serializeServiceItem={ this.serializeServiceItem } />);

    this.setState({
      serviceItems: serviceItems
    });
  },

  serializeServiceItem: function() {
    var jsonData = {
      "service_item" : this.state.service_items,
      "customer" : this.state.customer,
      "manufacturer" : this.state.manufacturer,
      "model" : this.state.model
    }
    return (jsonData);    
  },

  serializeFormData: function() {
    return( this.state.serviceItems.map(function(item) {
      return (item.serializeServiceItem);
    }));
  }
});

var ServiceItem = React.createClass({
  render: function() {
    return (
      <div className="row">
        <div className="col-sm-3">
          <div className="form-group">
            <label>Service Item </label>
            <select multiple name="service_item" selected={ this.state.service_items } className="form-control">
              <option>Oil Change</option>
              <option>Tire Rotation</option>
              <option>New Wiper Blades</option>
            </select>
          </div>
        </div>

        <div className="col-sm-3">
          <div className="form-group">
            <label>Customer </label>
            <select name="customer" selected={ this.state.customer } className="form-control">
              <option>Troy</option>
              <option>Dave</option>
              <option>Brandon</option>
            </select>
          </div>
        </div>        

        <div className="col-sm-3">
          <div className="form-group">
            <label>Manufacturer </label>
            <div className="input-group">
               <input name="manufacturer" value={ this.state.manufacturer } onChange={ this.setManufacturer } type="text" className="form-control" />
            </div>
          </div>
        </div>


        <div className="col-sm-3">
          <div className="form-group">
            <label>Model </label>
            <div className="input-group">
               <input name="model" value={ this.state.model } onChange={ this.setModel } type="text" className="form-control" />
            </div>
          </div>
        </div>
      </div>
    );
  },

  getInitialState: function() {
    return({
      service_items: [],
      customer: "",
      manufacturer: "",
      model: ""
    });
  },

  setModel: function(event) {
    this.setState({ model: event.target.value });
  },

  setManufacturer: function(event) { 
    this.setState({ manufacturer: event.target.value });
  },

  setCustomer: function(event) {
    this.setState({ customer: event.target.selected });
  },

  setServiceItems: function(event) {
    this.setState({ service_items: event.target.selected });
  }
});

ReactDOM.render(
  <ServiceForm />,
  document.getElementById('container')
);

解决方案

https://jsfiddle.net/morahood/cp569zg6/19/

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    你“可能”在这里把事情复杂化了。 &lt;form&gt; 的 DOM 元素实际上可以被视为所有内部 &lt;input&gt; 元素的数组。换句话说,如果你有:

    render: function() {
      return (
        <form ref="form">
          ...
        </form>
      );
    }
    

    您的所有输入元素都可以通过以下方式访问:

    serialized = {}
    for (var i in this.refs.form) {
      var input = this.refs.form[i];
      serialized[input.name] = input.value;
    }
    

    这可能无法为您提供足够的灵活性。更好的解决方案可能是在您的组件实例中定义返回输入值的方法:

    var ServiceForm = React.createClass({
      serializeFormData: function() {
        return {
          foo: this.refs.foo.serialize()
        };
      },
    
      render: function() {
         var foo = this.state.foo;
         return (
           <ServiceItem data={foo} ref="foo" />
         );
      }
    });
    
    var ServiceItem = React.createClass({
      serialize: function() {
        return {
          model: this.refs.model.value,
          ...
        }
      },
    
      render: function() {
        var model = this.props.data.model;
        return (
          <input ref="model" value={model} ... />
        );
      }
    });
    

    如果您需要多个服务项,您可能需要依赖 this.props.children 而不是 this.refs 来访问每个组件实例:

    var ServiceContainer = React.createClass({
      collectFormData: function() {
        return this.refs.form.serialize();
      },
    
      renderServiceItem: function(item, i) {
        return (
          <ServiceItem data={item} key={i} />
        );
      },
    
      render: function() {
        // Assuming you've moved all your state logic into this ServiceContainer
        var serviceItems = this.state.serviceItems;
        return (
          <ServiceForm ref="form">
            {serviceItems.map(this.renderServiceItem)}
          </ServiceForm>
        );
      }
    });
    
    var ServiceForm = React.createClass({
      serialize: function() {
        return React.Children.map(this.props.children, function(item) {
          return item.serialize();
        });
      },
    
      render: function() {
        return (
          <div>{this.props.children}</div>
        );
      }
    });
    
    var ServiceItem = React.createClass({
      serialize: function() {
        // You can still access your input elements through refs in here
        ...
      },
    
      render: function() {
        ...
      }
    });
    

    请注意,我在这里使用React.Children 而不是简单地使用this.props.children,因为当只有一个孩子时,children 不是数组(请参阅:https://facebook.github.io/react/tips/children-props-type.html)。

    【讨论】:

    • 谢谢你。我觉得这让我离目标更近了。我正在尝试实施您的第二个建议,最后能够与返回的 JSON 结构有一些相似之处。不幸的是,子组件返回为空。我已经更新了我的小提琴jsfiddle.net/morahood/0ptdpfu7/115。如果我有多个 ServiceItem,是否需要在 serializeFormData 方法中遍历每一个?
    • 我添加了一个示例,说明如何使用this.props.children 让您访问每个ServiceItem,而不是使用this.refs
    • 感谢您的帮助。你的回答是让我走上正确的轨道,让我去我需要去的地方。我想我只需要稍微消化一下这篇文章就可以完全理解发生了什么,但我觉得这是一个非常有用且有效的答案。
    【解决方案2】:

    您可以使用 react.rb 和 reactive-record 来为您完成所有这些工作。 http://reactrb.org

    【讨论】:

      猜你喜欢
      • 2016-06-17
      • 2018-06-18
      • 2011-08-13
      • 1970-01-01
      • 2020-08-25
      • 2021-08-23
      • 2013-02-04
      • 2018-06-17
      • 1970-01-01
      相关资源
      最近更新 更多