2017-11-18 41 views
1

這是一個長期的問題,給我一個頭痛的解決。如何傳遞輸入值以形成onSubmit而不使用呈現多個表單的組件中的狀態?

我正在製作投票應用程序。在頁面上會列出您可以投票的投票。每個輪詢都是一個由輸入單選按鈕組成的表單,表示該輪詢可用的不同選項。

我之前做的是將選擇的選項保存到this.state.value中,然後在提交表單時將其作爲參數傳遞給動作創建者。

這種方法的問題是,如果我點擊一個民意調查的選項,然後點擊提交另一個民意調查,我已經提交錯誤的民意測驗的錯誤選項。

有沒有辦法將輸入值傳遞到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

更「反應」模式將分解成更多的組件。
a Poll是一個組件,PollOption也可以是一個組件。
每個可以在內部處理狀態。

這將允許您保持全局狀態在您的App或其他一些像redux這樣的狀態管理器,它將保存您的所有投票並且每個可以引用所選選項(id)。

另一件值得指出的事情是,你傾向於在每個render調用上通過一個新的函數引用。
例如:

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

這被認爲是不好的做法,因爲你可以用Reconciliation and The Diffing Algorithm of react干擾。

當你把它分解成每個都可以使用它的道具回擊一個回調的組件時,你不需要這樣傳遞處理函數。

這裏是一個小例子與數據:

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

相關問題