如何将道具传递给 redux-form 中的装饰形式?

How to pass down props to decorated form in `redux-form`?

我正在使用 redux-form 构建向导表单并遇到 onClick 处理程序未作为道具从容器组件传递到表单中的问题。

我在过去的几个小时里搜索了文档无济于事......似乎有一种方法可以将 props 传递到装饰的 HOC 中,但我看不到任何这样的例子。

相关代码如下:

RadioButtons.js(调用onClick函数的地方)

const RadioButtons = props => {
  const { question, handleClick } = props;
  const radioClassNames = classNames({
    "radio-button": true,
    "radio-button-image-wrapper": question.image,
    "radio-button-text-wrapper": !question.image,
  });
  return (
    <div className="radio-buttons">
      <div className="radio-buttons-wrapper">
        {question.options.map((option, index) => (
          <div className={radioClassNames} key={index} onClick={handleClick}>
            <Field
              component="input"
              type="radio"
              name={`option-${option}`}
              id={`option-${option}`}
            />
            <label className="radio-button-text" htmlFor={option}>
              {option}
            </label>
        )}
          </div>
        )
        )}
      </div>
    </div>
  )
}

RadioButtons.PropTypes = {
  question: PropTypes.object.isRequired,
  handleClick: PropTypes.func,
}

export default RadioButtons;

当我在 React DevTools 中检查这个组件时,它没有这个属性。它也不在以下组件中...

Question.js(这决定了呈现什么样的问题;滑块、单选按钮、text/email/tel 输入等)

const Question = props => {
  const { handleSubmit, onBlur, question, handleClick } = props;
  return (
    <div className={`question question-${question.name}`}>
      <form className={props.className} onSubmit={handleSubmit}>
        <div className="question-wrapper">
          <label className={`question-label-${question.name}`}>{question.text}</label>
          { question.image === true && question.type !== "checkbox" && question.type !== "radio" &&
            <img className="question-image" src={`/images/${question.name}.png`} />
          }
          { question.type === "radio" && <RadioButtons question={question} handleClick={handleClick} /> }
          { question.type === "range" && <Slider question={question} /> }
          { question.type !== "checkbox" && question.type !== "radio" && question.type !== "range" &&
            <Field
              component={question.component}
              type={question.type}
              name={question.name}
              placeholder={question.placeholder}
              onBlur={onBlur}
            />
          }
        </div>
      </form>
    </div>
  )
}

Question.PropTypes = {
  handleSubmit: PropTypes.func,
  onBlur: PropTypes.func,
  question: PropTypes.object.isRequired,
  handleClick: PropTypes.func,
}

export default reduxForm({
  form: 'quiz',
  destroyOnUnmount: false,
  forceUnregisterOnUnmount: true,
})(Question);

QuestionContainer.jsQuestion.js 的包装器;确定是在屏幕上呈现多个问题还是只呈现一个)。 handleClick道具终于出现了!

const QuestionContainer = props => {
  const { question, handleClick } = props;

  const questionClassNames = classNames({
    'question-wrapper': true,
    'question-single': props.question !== 'combined',
    'question-multiple': props.question === 'combined',
  });

  const renderQuestions = question => {
    if (question.component === 'combined') {
      return (
       <div className="multi-question-container">
         <label className="multi-question-label">{question.text}</label>
         <div className="multi-question-wrapper">
           {question.subQuestions.map((subQuestion, index) => {
             const newName = `${question.name}-${subQuestion.name}`;
             const newSubQuestion = Object.assign({}, subQuestion, { name: newName })
             return (
               <Question
                 question={newSubQuestion}
                 key={index}
                 className={questionClassNames}
                 handleClick={handleClick}
               />
             )
           })}
         </div>
       </div>
      )} else {
       return (
         <Question
           question={question}
           className={questionClassNames}
         />
       )
     }
  }

  return (
    <div className="question-container">
      {renderQuestions(question)}
    </div>
  )
}

QuestionContainer.PropTypes = {
  question: PropTypes.object.isRequired,
  handleClick: PropTypes.func,
}

export default QuestionContainer;

但是... handleClick 道具 没有出现 Quiz 组件中,我实际上需要用它来调用nextScreen():

class Quiz extends Component {
  constructor(props) {
    super(props);
    this.state = {
      screen: 0
    }
  }

  nextScreen = () => {
    console.log("nextScreen");
    if (this.state.screen < data.questions.length) {
      this.setState({screen: this.state.screen + 1});
    } else {
      this.props.calculateResult(this.props.form.quiz.values);
      this.props.history.push('/congratulations');
    }
  }

  lastScreen = () => {
    if (this.state.screen > 1) {
      this.setState({screen: this.state.screen - 1});
    } else {
      this.props.history.push('/');
    }
  }

  render() {

    const currentQuestion = Object.assign({}, data.questions[this.state.screen]);

    let arrowSource = `/images/arrow-button-${arrowColor}.png`;

    return (
      <div>
        <div className="quiz-wrapper">
          <ArrowButton src={arrowSource} route="back" handleClick={this.lastScreen} />
          <div className="quiz-wrapper-inner">
            <QuestionContainer question={currentQuestion} handleClick={this.nextScreen} />
          </div>
          <ArrowButton src={arrowSource} route="forward" handleClick={this.nextScreen} />
        </div>
      </div>
    )
  }
}

const mapStateToProps = state => {
  return {
    form: state.form,
  }
};

const mapDispatchToProps = dispatch => {
  return {
    calculateResult: answers => dispatch(calculateResult(answers)),
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(Quiz);

ArrowButton 组件上的 handleClick 属性工作正常,可以顺利调用 nextScreen()/lastScreen()。只是在 QuestionContainer 才没有流传下来。

您在 QuestionsContainer 组件中有两个 Questions 组件实例。当满足 question.component === 'combined' 条件时,您将传递 handleClick 道具,但 否则不会 ,因此它在您的 Questions 组件中不可用。

你还需要在 else 条件下将 handleClick 作为 prop 传递

const renderQuestions = question => {
    if (question.component === 'combined') {
      return (
       <div className="multi-question-container">
         <label className="multi-question-label">{question.text}</label>
         <div className="multi-question-wrapper">
           {question.subQuestions.map((subQuestion, index) => {
             const newName = `${question.name}-${subQuestion.name}`;
             const newSubQuestion = Object.assign({}, subQuestion, { name: newName })
             return (
               <Question
                 question={newSubQuestion}
                 key={index}
                 className={questionClassNames}
                 handleClick={handleClick}
               />
             )
           })}
         </div>
       </div>
      )} else {
       return (
         <Question
           question={question}
           className={questionClassNames}
           handleClick={handleClick}
         />
       )
     }
  }