根据状态在React中渲染不同的子组件

时间:2018-01-07 22:59:06

标签: javascript reactjs

我在React做了一个简短的测验,每个问题都是一个不同的组件。每次回答问题时,我都希望将该组件切换为下一个问题组件。这是我现在的根组件:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import PanelOne from './components/PanelOne';
import PanelTwo from './components/PanelTwo';
import PanelThree from './components/PanelThree';
import PanelFour from './components/PanelFour';
import Results from './components/Results';

class App extends Component {

  constructor (props) {
    super (props);
    this.state = {
      collaborator: 0,
      pilot: 0,
      producer: 0,
      harmonizer: 0,
      counter: 0
      answers: {
        answersOne: [
          {answer: 'Team building', archetype: 'collaborator'},
          {answer: 'Directing strategy', archetype: 'pilot'},
          {answer: 'Driving task completion', archetype: 'producer'},
          {answer: 'Keeping processes and relationships running smoothly', archetype: 'harmonizer'}
        ],

        answersTwo: [
          {answer: 'Setting a clear direction and creating a personal brand', archetype: 'collaborator'}
          {answer: 'Making space for others and planning for the longterm', archetype: 'pilot'}
          {answer: 'Connecting with team members and innovating', archetype: 'producer'}
          {answer: 'Accepting ambiguity and addressing conflict', archetype: 'harmonizer'}
        ],

        answersThree: [
          {answer: 'Settings where team members are seeking coaching and development', archetype: 'collaborator'}
          {answer: 'Ambiguous and high growth environments', archetype  'pilot'}
          {answer: 'Organizations with clear structures and processes', archetype: 'producer'}
          {answer: 'Volatile settings that need to be tamed'  archetype: 'harmonizer'}
        ],

        answersFour: [
          {answer: 'Settings where unilateral decision making is required', archetype: 'collaborator'}
          {answer: 'Conservative environments that discourage  innovation', archetype: 'pilot'}
          {answer: 'Teams where each member desires independence'  archetype: 'producer'}
          {answer: 'Anywhere tough feedback needs to be given'  archetype: 'harmonizer'}
        ]
      }
    }
    this.onSelect = this.onSelect.bind(this);
  }

  onSelect(value) {
    switch (value) {
      case 'collaborator':
        this.setState(prevState => {
          return {
            collaborator: prevState.collaborator + 1,
            counter: prevState.counter + 1
          }
        });
        break;
      case 'pilot':
        this.setState(prevState => {
          return {
            pilot: prevState.pilot + 1,
            counter: prevState.counter + 1
          }
        });
        break;
      case 'producer':
        this.setState(prevState => {
          return {
            producer: prevState.producer + 1,
            counter: prevState.counter + 1
          }
        });
        break;
      case 'harmonizer':
        this.setState(prevState => {
          return {
            harmonizer: prevState.harmonizer + 1,
            counter: prevState.counter + 1
          }
        });
        break;
    }
  }

  render() {
    switch (this.state.counter) {
      case 0:
        return (
          <div>
            <PanelOne answers={this.state.answers.answersOne} onSelect={this.onSelect}/>
          </div>
        );
        break;
      case 1:
        return (
          <div>
            <PanelTwo answers={this.state.answers.answersTwo} onSelect={this.onSelect}/>
          </div>
        );
        break;
      case 2:
        return (
          <div>
            <PanelThree answers={this.state.answers.answersThree}  onSelect={this.onSelect}/>
          </div>
        );
        break;
      case 3:
        return (
          <div>
            <PanelFour answers={this.state.answers.answersFour}  onSelect={this.onSelect}/>
          </div>
        );
        break;
      case 4:
        return (
          <div>
            <Results />
          </div>
        )
    }
  }
}

export default App;

我的Form组件如下所示:

import React from 'react';
import PropTypes from 'prop-types';
import { RadioGroup, RadioButton } from 'react-radio-buttons';

function Form (props) {
    return (
        <RadioGroup onChange={props.onSelect}>
            {
                props.answers.map(answer => 
                    <RadioButton key={answer.answer} value={answer.archetype}>
                        {answer.answer}
                    </RadioButton>
                )
            }
        </RadioGroup>
    )
}

Form.propTypes = {
  answers: PropTypes.array.isRequired,
  onSelect: PropTypes.func.isRequired
};

export default Form;

截至目前,每个Panel组件以及Results组件都是空白的,Panel组件只是Form的不同版本的容器。我将它们作为容器的原因纯粹是因为我可以对每个面板进行不同的设置(这是一个好主意吗?)。

正如我所提到的,我想要的行为是,当选择一个答案时,将根据状态的变化呈现一个新组件。我猜这不会发生,因为setState()是异步的,依赖它的变化不会立即发生。我的问题是如何实现我想要的行为?我是否使用生命周期方法?在这种情况下,我仍然有点模糊这些工作以及我如何使用它们。我的第二个问题(我仍然在寻找我的第一个问题的答案)无论我正在做什么都是好的做法。如果我只使用更新的道具呈现Form的不同版本,我是否可以将它们设置为不同的样式?

2 个答案:

答案 0 :(得分:0)

对于遇到这种情况的人:我犯了一个非常愚蠢的错误,在我的answers对象中遗漏了一些逗号。当我修复它时,一切都正常运行。

答案 1 :(得分:0)

在你当前的问题中,事情的编写方式可能不同,例如你的onSelect方法现在是一个巨大的switch语句,虽然你的原型和状态属性似乎匹配,而不是这个巨大的开关,你可以改变你的声明如下:

onSelect(value) {
  this.setState(prevState => {
    return {
      [value]: prevState[value] + 1,
      counter: prevState.counter + 1
    }
  });
}

这样做会完全一样,因为archetype完全匹配你的属性,并且你在每一步都会增加计数器,不需要多次做事;)

这甚至适用于你的渲染方法,不需要一个巨大的开关,只需要一个字典与你的面板匹配状态(如果你想这样做),你可以这样做

// provided you have a const on top, like
const questionsMap = { 0: 'One', 1: 'Two', 2: 'Three', 3: 'Four' };

render() {
  let { counter, answers } = this.state;
  if (!questionsMap[counter]) {
    // nothing matches, return the results panel
    return (<div>
        <Results />
      </div>);
  }
  let mappedValue = questionsMap['counter'];
  return React.createElement( 'Panel' + mappedValue , { answers: answers['answers' + mappedValue], onSelect: this.onSelect });
}

这确实减少了很多代码重复,并且从长远来看会简化你的事业,但是在我的答案的原始部分,我也想给你一个不同的方法来构建一切;)

我主要想为您提供一种构建代码的不同方式。对不起,我不得不重新创建RadioButton&amp; RadioGroup新的,但我没有立刻找到正确的cdn(所以忽略那些实现;)

主要是,这个应用程序还会给你一个小测验,可以跟踪正确答案,给出了哪些答案(但是,不跟踪正确的答案;)

您可以看到此代码比您的代码更紧凑,并使用了一些es6功能,这些功能真正充分利用了反应和功能。 jsx,你可以从使用功能组件中的道具解构(只是一种乐趣)和letconst关键字的使用开始。

我知道它并没有直接回答你的问题,但我只是想告诉你,你不需要这么多的代码重复,只需要过去的样式作为道具(或者你将有超过20个问题的20多个组件)未来,这是你永远不想维护的代码)

Jsx&amp; React在可重用性方面设置得很高,如果你三思而后行,那么想想你可以做些什么来统一你的实现

const RadioGroup = ({ onChange, children = [] }) => {
  return <fieldset>
  { children.map( child => {
    return React.createElement( child.type, {...child.props, key: child.key, onChange } ); }
    ) }
  </fieldset>;
};

const RadioButton = ({ onChange, children, value }) => {
  return <button onClick={(e) => onChange( value )}>{ children }</button>;
};

const Results = ({ correctAnswers, givenAnswers, currentQuestion }) => {
  return <div>
    <div><b>Correct answsers:</b>{ correctAnswers }</div>
    <div><b>Answers given:</b>{ givenAnswers.join(', ') }</div>
    <div><b>False answers:</b>{currentQuestion - correctAnswers}</div>
  </div>
};

const questions = [
  { question: 'Are you on stack overflow?', answers: ['yes', 'no'], correct: 'yes' },
  { question: 'Are you looking for help?', answers: ['yes', 'no'], correct: 'yes' },
  { question: 'Did you find your answer?', answers: ['yes', 'no', 'maybe'], correct: 'maybe' }
];

const Question = ({ question, answers, correct, onSelected }) => {
  return (<div>
    <h2>{ question }</h2>
    <RadioGroup onChange={(e) => onSelected( correct === e, e, correct) }>
    { answers.map( answer => <RadioButton key={answer} value={answer}>{ answer }</RadioButton> ) }
    </RadioGroup>
   </div>);
};

class App extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {
      currentQuestion: 0,
      correctAnswers: 0,
      givenAnswers: []
    };
  }
  onSelected( wasCorrect, answerGiven, correctAnswer ) {
    this.setState( prevState => ({
      currentQuestion: prevState.currentQuestion + 1,
      correctAnswers: prevState.correctAnswers + (wasCorrect ? 1 : 0),
      givenAnswers: [...prevState.givenAnswers, answerGiven]
    }));
  }
  render() {
    let { currentQuestion } = this.state;
    let { questions } = this.props;
    if (currentQuestion < questions.length) {
      return <div><Question {...questions[currentQuestion]} onSelected={(...args) => this.onSelected(...args)} /></div>;
    }
    return <div><Results {...this.state} /></div>
  }
}

ReactDOM.render( <App questions={questions} />, document.querySelector('#container') );
<script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<script id="prop-types" src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.0/prop-types.js"></script>
<script id="classnames" src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.js"></script>
<div id="container"></div>