如何传递输入值以形成onSubmit而不使用呈现多个表单的组件中的状态?

问题描述:

这是一个长期的问题,给我一个头痛的解决。如何传递输入值以形成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

更“反应”模式将分解成更多的组件。
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