2017-11-21 30 views
0

私は、ユーザーが自分の友達を招待できるようにするコンポーネントを構築しています。Redux動的にReduxフォームを作成する

コンポーネントの仕様は、友人の電子メールと企業のためのいくつかの入力フォームと、入力フォームを追加するボタンと、すべてのフォームを遠隔から提出するボタンを持つことです。フォームが送信されると、サーバーからの応答が受信されるまで、各フォームにスピンナが表示されます。その時点で、送信が成功し、フォームが消え、エラーがあった場合はエラーが表示されます。

私は次のことに固執しています:フォームをリモートでReduxフォームで送信するには、その名前を送信コンポーネントに渡す必要があります。私はプログラム的にフォームを作成したい。名前は自動インクリメントされた整数で、フォーム管理コンポーネントによって作成され、子フォームに小道具として渡されます。しかし、フォームをエクスポートしようとするとthis.props.nameという名前を参照すると、エラーが発生します。 'Uncaught TypeError:プロパティ' props 'undefined'を読み取ることができません - "this"は未定義です。

質問:

  1. が有効なこの問題を解決するための私のアプローチです、または私は基本的なレベルで異なる何かをする必要がありますか?
  2. どうすればこの問題を回避できますか?私はそれがスコープエラーだと思いますか?

マイ成分:

管理コンポーネント(作成し、フォームを削除し、それらを送信、等):

import React, { Component } from 'react'; 
import { connect } from 'react-redux' 
import { submit } from 'redux-form' 
import * as actions from '../../actions'; 
import InviteForm from './inviteForm'; 

class InvitationFormManager extends Component { 

    const buildForms = (length) =>{ 
    for (let i = 0; i< length; i++) 
     { 
      this.setState({forms:[...this.state.forms, <InviteForm key={i} name={i}>]}; 
     } 
    } 

    const addForm =() =>{ 
    this.setState({forms:[...this.state.forms, <InviteForm key={(this.state.forms.length + 1)} name={(this.state.forms.length + 1)}>]}); 
    } 

    const formSubmit = (form) => 
    { 
    dispatch(submit(form.name)) 
    .then(this.setState({ 
     forms: this.state.forms.filter(f => f.name !== form.name) 
    })) 
    } 
    const submitForms =(){ 
    for(let form of this.state.forms){formSubmit(form)} 
    } 

    constructor(props) { 
     super(props); 
     this.state = {forms:[]}; 
    } 

    componentWillMount(){ 
    buildForms(3) 
    } 

    render() { 
    return (<div> 
       <h5 className="display-6 text-center">Invite your team</h5> 
       {this.state.forms} 
       <br /> 
       <button 
       type="button" 
       className="btn btn-primary" 
       onClick={submitForms} 
       > 
       Invite 
       </button> 
       <button 
       type="button" 
       className="btn btn-primary" 
       onClick={addForm} 
       > 
       + 
       </button> 
      </div> 
    ); 
    } 
} 


export default connect(actions)(InvitationFormManager) 

フォーム成分:

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 
import { Field, reduxForm } from 'redux-form'; 
import * as actions from '../../actions'; 
import { Link } from 'react-router'; 

const renderField = ({ 
    input, 
    label, 
    type, 
    meta: { touched, error, warning } 
}) => (
    <fieldset className="form-group"> 
     <label htmlFor={input.name}>{label}</label> 
     <input className="form-control" {...input} type={type} /> 
     {touched && error && <span className="text-danger">{error}</span>} 
    </fieldset> 
); 

class InviteForm extends Component { 
    constructor(props) { 
     super(props); 
     this.name = this.name.bind(this); 
    } 

    handleFormSubmit(props) { 
     this.props.sendInvitation(props); 
    } 

    render() { 
     if (this.props.submitting) { 
      return (
       <div className="dashboard loading"> 
        <Spinner name="chasing-dots" /> 
       </div> 
      ); 
     } 
     const { formName, handleSubmit } = this.props; 
     return (
      <div className="form-container text-center"> 
       <form 
        className="form-inline" 
        onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}> 
        <div className="form-group"> 
         <Field 
          name="email" 
          component={renderField} 
          type="email" 
          label="Email" 
         /> 
         <Field 
          name="company" 
          component={renderField} 
          type="text" 
          label="Company" 
         /> 
        </div> 
       </form> 
       <div> 
        {this.props.errorMessage && 
         this.props.errorMessage.invited && (
          <div className="error-container"> 
           Oops! {this.props.errorMessage.invited} 
          </div> 
         )} 
       </div> 
      </div> 
     ); 
    } 
} 
function validate(values) { 
    let errors = {}; 

    if (values.password !== values.password_confirmation) { 
     errors.password = "Password and password confirmation don't match!"; 
    } 

    return errors; 
} 
function mapStateToProps(state) { 
    return { 
     errorMessage: state.invite.error, 
     submitting: state.invite.submitting 
    }; 
} 

InviteForm = reduxForm({ 
    form: this.props.name, 
    validate 
})(InviteForm); 
export default connect(mapStateToProps, actions)(InviteForm); 

答えて

1

答えは、RTFM。 ReduxフォームはFieldArraysとしてこの機能を持っています。

Reduxのフォーム7.0.4のための例はここにある:彼らはそれを後で移動する場合https://redux-form.com/7.0.4/examples/fieldarrays/

、ここにある:

FieldArraysForm.js

import React from 'react' 
import { Field, FieldArray, reduxForm } from 'redux-form' 
import validate from './validate' 

const renderField = ({ input, label, type, meta: { touched, error } }) => 
    <div> 
    <label> 
     {label} 
    </label> 
    <div> 
     <input {...input} type={type} placeholder={label} /> 
     {touched && 
     error && 
     <span> 
      {error} 
     </span>} 
    </div> 
    </div> 

const renderHobbies = ({ fields, meta: { error } }) => 
    <ul> 
    <li> 
     <button type="button" onClick={() => fields.push()}> 
     Add Hobby 
     </button> 
    </li> 
    {fields.map((hobby, index) => 
     <li key={index}> 
     <button 
      type="button" 
      title="Remove Hobby" 
      onClick={() => fields.remove(index)} 
     /> 
     <Field 
      name={hobby} 
      type="text" 
      component={renderField} 
      label={`Hobby #${index + 1}`} 
     /> 
     </li> 
    )} 
    {error && 
     <li className="error"> 
     {error} 
     </li>} 
    </ul> 

const renderMembers = ({ fields, meta: { error, submitFailed } }) => 
    <ul> 
    <li> 
     <button type="button" onClick={() => fields.push({})}> 
     Add Member 
     </button> 
     {submitFailed && 
     error && 
     <span> 
      {error} 
     </span>} 
    </li> 
    {fields.map((member, index) => 
     <li key={index}> 
     <button 
      type="button" 
      title="Remove Member" 
      onClick={() => fields.remove(index)} 
     /> 
     <h4> 
      Member #{index + 1} 
     </h4> 
     <Field 
      name={`${member}.firstName`} 
      type="text" 
      component={renderField} 
      label="First Name" 
     /> 
     <Field 
      name={`${member}.lastName`} 
      type="text" 
      component={renderField} 
      label="Last Name" 
     /> 
     <FieldArray name={`${member}.hobbies`} component={renderHobbies} /> 
     </li> 
    )} 
    </ul> 

const FieldArraysForm = props => { 
    const { handleSubmit, pristine, reset, submitting } = props 
    return (
    <form onSubmit={handleSubmit}> 
     <Field 
     name="clubName" 
     type="text" 
     component={renderField} 
     label="Club Name" 
     /> 
     <FieldArray name="members" component={renderMembers} /> 
     <div> 
     <button type="submit" disabled={submitting}> 
      Submit 
     </button> 
     <button type="button" disabled={pristine || submitting} onClick={reset}> 
      Clear Values 
     </button> 
     </div> 
    </form> 
) 
} 

export default reduxForm({ 
    form: 'fieldArrays', // a unique identifier for this form 
    validate 
})(FieldArraysForm) 

validate.js

const validate = values => { 
    const errors = {} 
    if (!values.clubName) { 
    errors.clubName = 'Required' 
    } 
    if (!values.members || !values.members.length) { 
    errors.members = { _error: 'At least one member must be entered' } 
    } else { 
    const membersArrayErrors = [] 
    values.members.forEach((member, memberIndex) => { 
     const memberErrors = {} 
     if (!member || !member.firstName) { 
     memberErrors.firstName = 'Required' 
     membersArrayErrors[memberIndex] = memberErrors 
     } 
     if (!member || !member.lastName) { 
     memberErrors.lastName = 'Required' 
     membersArrayErrors[memberIndex] = memberErrors 
     } 
     if (member && member.hobbies && member.hobbies.length) { 
     const hobbyArrayErrors = [] 
     member.hobbies.forEach((hobby, hobbyIndex) => { 
      if (!hobby || !hobby.length) { 
      hobbyArrayErrors[hobbyIndex] = 'Required' 
      } 
     }) 
     if (hobbyArrayErrors.length) { 
      memberErrors.hobbies = hobbyArrayErrors 
      membersArrayErrors[memberIndex] = memberErrors 
     } 
     if (member.hobbies.length > 5) { 
      if (!memberErrors.hobbies) { 
      memberErrors.hobbies = [] 
      } 
      memberErrors.hobbies._error = 'No more than five hobbies allowed' 
      membersArrayErrors[memberIndex] = memberErrors 
     } 
     } 
    }) 
    if (membersArrayErrors.length) { 
     errors.members = membersArrayErrors 
    } 
    } 
    return errors 
} 

export default validate 
関連する問題