2017-11-18 5 views
1

これは長年の問題であり、私に解決のための頭痛を与えています。複数のフォームをレンダリングするコンポーネントでstateを使用せずにonSubmitフォームに入力値を渡す方法は?

私は投票アプリを作っています。このページに投票できる投票のリストが表示されます。各投票は、投票に使用できるさまざまなオプションを表す入力ラジオボタンで構成されるフォームです。

私がこれまで行ってきたことは、コンポーネント状態に選択したオプションをthis.state.valueに保存してから、フォームを送信するときにアクション作成者に引き渡すことでした。

このアプローチの問題点は、1つの投票のオプションをクリックして別の投票で送信をクリックすると、間違った投票に誤ったオプションを実際に提出したことです。

入力値をフォームonSubmitに渡す方法はありませんか?

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 
import * as actions from '../../actions'; 
import Loading from '../Loading'; 

class MyPolls extends Component { 
    constructor(props) { 
    super(props); 
    this.state = { 
     skip: 0, 
     isLoading: true, 
     isLoadingMore: false, 
     value: '' 
    }; 
    this.handleSubmit = this.handleSubmit.bind(this); 
    this.handleChange = this.handleChange.bind(this); 
    } 

    componentDidMount() { 
    this.props.fetchMyPolls(this.state.skip) 
     .then(() => { 
     setTimeout(() => { 
      this.setState({ 
      skip: this.state.skip + 4, 
      isLoading: false 
      }); 
     }, 1000); 
     }); 
    } 

    sumVotes(acc, cur) { 
    return acc.votes + cur.votes 
    } 

    loadMore(skip) { 
    this.setState({ isLoadingMore: true }); 

    setTimeout(() => { 
     this.props.fetchMyPolls(skip) 
     .then(() => { 
      const nextSkip = this.state.skip + 4; 
      this.setState({ 
      skip: nextSkip, 
      isLoadingMore: false 
      }); 
     }); 
    }, 1000); 
    } 

    handleSubmit(title, e) { 
    // console.log(e.target); 
    e.preventDefault(); 
    const vote = { 
     title, 
     option: this.state.value 
    }; 

    console.log(vote) 
    } 

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

    renderPolls() { 
    return this.props.polls.map(poll => { 
     return (
     <div 
      className='card' 
      key={poll._id} 
      style={{ width: '350px', height: '400px' }}> 
      <div className='card-content'> 
      <span className='card-title'>{poll.title}</span> 
      <p> 
       Total votes: {poll.options.reduce((acc, cur) => { return acc + cur.votes }, 0)} 
      </p> 
      <form onSubmit={e => this.handleSubmit(poll.title, e)}> 
       {poll.options.map(option => { 
       return (
        <p key={option._id}> 
        <input 
         name={poll.title} 
         className='with-gap' 
         type='radio' 
         id={option._id} 
         value={option.option} 
         onChange={this.handleChange} 
        /> 
        <label htmlFor={option._id}> 
         {option.option} 
        </label> 
        </p> 
       ) 
       })} 

       <button 
       type='text' 
       className='activator teal btn waves-effect waves-light' 
       style={{ 
        position: 'absolute', 
        bottom: '10%', 
        transform: 'translateX(-50%)' 
       }} 
       > 
       Submit 
       <i className='material-icons right'> 
        send 
       </i> 
       </button> 
      </form> 
      </div> 
      <div className='card-reveal'> 
      <span className='card-title'>{poll.title} 
       <i className='material-icons right'>close</i> 
      </span> 
      <p> 
       dsfasfasdf 
      </p> 
      </div> 
     </div> 
    ) 
    }) 
    } 

    render() { 
    return (
     <div className='center-align container'> 
     <h2>My Polls</h2> 
     {this.state.isLoading ? <Loading size='big' /> : 
     <div 
      style={{ 
      display: 'flex', 
      flexWrap: 'wrap', 
      justifyContent: 'space-evenly', 
      alignItems: 'center', 
      alignContent: 'center' 
      }}> 
      {this.renderPolls()} 
     </div>} 
     <div className='row'> 
      {this.state.isLoadingMore ? <Loading size='small' /> : 
      <button 
      className='btn red lighten-2 wave-effect waves-light' onClick={() => this.loadMore(this.state.skip)}> 
      Load More 
      </button>} 
     </div> 
     </div> 

    ); 
    } 
} 

function mapStateToProps({ polls }) { 
    return { polls } 
} 

export default connect(mapStateToProps, actions)(MyPolls); 

アプリのデモ:https://voting-app-drhectapus.herokuapp.com/ (ログインする[email protected]とパスワード123を使用)

のGithubレポ:https://github.com/drhectapus/voting-app

私は、任意の提案に開いています。ありがとう!

+0

あなたの問題に対する典型的な解決策は、独立した 'state'とで(' 'コンポーネントクラスを作成することです' onSubmit() ')を使って各' Poll'に対してレンダリングします。 –

+0

私は、私が見落としていた簡単な解決策があることを知っていました。ありがとう! – doctopus

答えて

1

「React'ish」パターンが多いほど、より多くのコンポーネントに分解されます。
Pollは成分であり、PollOptionも成分であり得る。
それぞれが内部で状態を処理できます。

Appまたはすべての投票を保持し、それぞれが選択したオプション(id)を参照できるreduxなどの他の州のマネージャで、グローバルな状態を維持できます。

もう一つ注目すべきは、renderコールごとに新しい関数リファレンスを渡す傾向があることです。例えば
は:あなたはReconciliation and The Diffing Algorithm of reactに干渉する可能性があるため

onSubmit={e => this.handleSubmit(poll.title, e)} 

これは悪い習慣と考えられています。

それぞれ 小道具でコールバックを呼び出すことができるコンポーネントに分解すると、このようにハンドラを渡す必要はありません。ここで

は、あなたのデータの小さな例です。

const pollsFromServer = [ 
 
    { 
 
    _id: "5a0d308a70f4b10014994490", 
 
    title: "Cat or Dog", 
 
    _user: "59f21388843e737de3738a3a", 
 
    __v: 0, 
 
    dateCreated: "2017-11-16T06:30:34.855Z", 
 
    options: [ 
 
     { option: "Cat", _id: "5a0d308a70f4b10014994492", votes: 0 }, 
 
     { option: "Dog", _id: "5a0d308a70f4b10014994491", votes: 0 } 
 
    ] 
 
    }, 
 
    { 
 
    _id: "5a0c7941e655c22b8cce43d7", 
 
    title: "Blonde or Brunette?", 
 
    _user: "59f21388843e737de3738a3a", 
 
    __v: 0, 
 
    dateCreated: "2017-11-15T17:28:33.909Z", 
 
    options: [ 
 
     { option: "Blonde", _id: "5a0c7941e655c22b8cce43d9", votes: 0 }, 
 
     { option: "Brunette", _id: "5a0c7941e655c22b8cce43d8", votes: 0 } 
 
    ] 
 
    }, 
 
    { 
 
    _id: "5a0c7924e655c22b8cce43d4", 
 
    title: "Coke or Pepsi", 
 
    _user: "59f21388843e737de3738a3a", 
 
    __v: 0, 
 
    dateCreated: "2017-11-15T17:28:04.119Z", 
 
    options: [ 
 
     { option: "Coke", _id: "5a0c7924e655c22b8cce43d6", votes: 0 }, 
 
     { option: "Pepsi", _id: "5a0c7924e655c22b8cce43d5", votes: 0 } 
 
    ] 
 
    }, 
 
    { 
 
    _id: "5a0c78c2e655c22b8cce43d0", 
 
    title: "Favourite german car?", 
 
    _user: "59f21388843e737de3738a3a", 
 
    __v: 0, 
 
    dateCreated: "2017-11-15T17:26:26.724Z", 
 
    options: [ 
 
     { option: "BMW", _id: "5a0c78c2e655c22b8cce43d3", votes: 0 }, 
 
     { option: "Mercedes", _id: "5a0c78c2e655c22b8cce43d2", votes: 0 }, 
 
     { option: "Audi", _id: "5a0c78c2e655c22b8cce43d1", votes: 0 } 
 
    ] 
 
    } 
 
]; 
 

 
class Poll extends React.Component { 
 

 
    onSubmit = optionId => { 
 
    const { pollId, onSubmit } = this.props; 
 
    onSubmit(pollId, optionId); 
 
    }; 
 

 
    render() { 
 
    const { title, options, selectedOption } = this.props; 
 
    return (
 
     <div> 
 
     <h3>{title}</h3> 
 
     <ul> 
 
      {options.map((o, i) => { 
 
      return (
 
       <PollOption 
 
       isSelected={selectedOption === o._id} 
 
       onClick={this.onSubmit} 
 
       name={o.option} 
 
       optionId={o._id} 
 
       /> 
 
      ); 
 
      })} 
 
     </ul> 
 
     </div> 
 
    ); 
 
    } 
 
} 
 

 
class PollOption extends React.Component { 
 
    onClick =() => { 
 
    const { optionId, onClick } = this.props; 
 
    onClick(optionId); 
 
    }; 
 

 
    render() { 
 
    const { name, isSelected } = this.props; 
 
    const selectedClass = isSelected ? "selected" : ''; 
 
    return (
 
     <li 
 
     className={`poll-option ${selectedClass}`} 
 
     onClick={this.onClick} 
 
     > 
 
     {name} 
 
     </li> 
 
    ); 
 
    } 
 
} 
 

 
class App extends React.Component { 
 
    constructor(props) { 
 
    super(props); 
 
    this.state = { 
 
     polls: pollsFromServer, 
 
     submittedPolls: [] 
 
    }; 
 
    } 
 

 
    onPollSubmit = (pollId, optionId) => { 
 
    this.setState({ 
 
     submittedPolls: { 
 
     ...this.state.submittedPolls, 
 
     [pollId]: optionId 
 
     } 
 
    }); 
 
    }; 
 

 
    render() { 
 
    const { polls, submittedPolls } = this.state; 
 
    return (
 
     <div> 
 
     {polls.map((p, i) => { 
 
      const selectedPoll = submittedPolls[p._id]; 
 
      return (
 
      <Poll 
 
       selectedOption={selectedPoll} 
 
       pollId={p._id} 
 
       onSubmit={this.onPollSubmit} 
 
       title={p.title} 
 
       options={p.options} 
 
      /> 
 
     ); 
 
     })} 
 
     </div> 
 
    ); 
 
    } 
 
} 
 

 
ReactDOM.render(<App />, document.getElementById("root"));
.poll-option{ 
 
    cursor: pointer; 
 
    display: inline-block; 
 
    box-shadow: 0 0 1px 1px #333; 
 
    padding: 15px; 
 
} 
 
.selected{ 
 
    background-color: green; 
 
    color: #fff; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> 
 
<div id="root"></div>

+0

すばらしい説明。ありがとう! – doctopus

関連する問題