简体   繁体   English

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

[英]Render different child components in React based on state

I'm making a short quiz in React, with each question as a different component. 我在React中做一个简短的测验,每个问题都是一个不同的组成部分。 Each time a question is answered, I want to switch that component out for the next question component. 每次回答问题时,我都希望将该组件切换为下一个问题组件。 Here's my root component as it is right now: 这是我现在的根组件:

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;

My Form component looks like this: 我的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;

As of right now, each Panel component, as well as the Results component are all blank, with the Panel components only being containers for different versions of Form . 到目前为止,每个Panel组件以及Results组件都为空白,而Panel组件只是用于不同版本的Form容器。 The reason I have them as containers is purely so that I can style each panel differently (is this a good idea?). 我将它们作为容器的原因纯粹是为了可以对每个面板进行不同的样式设置(这是个好主意吗?)。

The behavior I want, as I mentioned, is that when an answer is selected a new component is rendered, based on the change in state. 正如我所提到的,我想要的行为是,当选择答案时,将根据状态变化呈现一个新组件。 I'm guessing that this isn't happening because setState() is asynchronous and changes relying on it won't happen immediately. 我猜这不会发生,因为setState()是异步的,依赖它的更改不会立即发生。 My question is how do I achieve the behavior I want? 我的问题是如何实现自己想要的行为? Do I use lifecycle methods? 我是否使用生命周期方法? I'm still somewhat fuzzy on how those work and how exactly I'd use them in this case. 对于这些工具的工作方式以及在这种情况下我将如何使用它们,我仍然有些模糊。 My secondary question (I am still looking for an answer to my first question regardless) is whether or not what I'm doing is good practice at all. 我的第二个问题(无论如何,我仍在寻找第一个问题的答案)是我所做的一切是否都是好的习惯。 If I rendered only different versions of Form with updated props, would I be able to style those differently from one another? 如果我仅使用更新的道具渲染了不同版本的Form ,我是否能够将它们的样式设置为彼此不同?

For anyone who comes across this: I made a really silly mistake and was missing some commas in my answers object, in my state. 对于遇到此问题的任何人:我犯了一个非常愚蠢的错误,并且在我的状态下,我的answers对象中缺少一些逗号。 When I fixed this, everything ran correctly. 当我解决此问题后,一切运行正常。

In your current question, things could be written differently, for example your onSelect method is now a huge switch statement, although your archetype and state properties seem to match, instead of this huge switch, you could change your statement to something like this: 在您当前的问题中,事情可以用不同的方式编写,例如,您的onSelect方法现在是一个巨大的switch语句,尽管您的原型和状态属性似乎匹配,但是可以代替以下巨大的开关来代替这个巨大的开关:

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

This would do the exact same thing, as archetype perfectly matches your properties, and the counter you increase anyhow in every step, no need to do things multiple times ;) 这将做完全相同的事情,因为原型可以完美地匹配您的属性,并且您在每一步中都会增加计数器,因此无需多次进行操作;)

This even goes for your render method as well, no need for a huge switch, just make a dictionary matching state with your panel (in case you wish to do it like that), and you could do this 这甚至也适用于您的render方法,不需要巨大的切换,只需将字典与面板的状态匹配即可(如果您希望那样做),则可以执行此操作

// 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 });
}

This really reduces a lot of code duplication, and will simplify your cause in the long run, however in the original part of my answer, I also like to give you a different way to structure everything ;) 从长远来看,这确实减少了很多代码重复,并且将简化您的原因,但是在我的答案的原始部分,我还想给您提供一种不同的方式来构建所有内容;)

I mainly want to offer you a different way of structuring your code. 我主要想为您提供一种不同的代码结构方式。 Sorry I had to recreate the RadioButton & RadioGroup new, but I didn't find the correct cdn at once (So ignore those implementations ;) ) 抱歉,我不得不重新创建RadioButtonRadioGroup new,但是我没有一次找到正确的CDN(因此请忽略那些实现;))

Mainly, this application will also give you a small quiz, that can track the correct answers, and which answers were given (however, doesn't track what the correct should have been ;)) 主要是,此应用程序还会为您提供一个小测验,它可以跟踪正确的答案以及给出的答案(但是,不会跟踪正确的答案;)

You can see that this code is a lot more compact than yours, and uses some es6 features, which really make full use of react & jsx as you can start by using destructuring of the props in the functional components (just a joy to work with) and the usage of let , const keywords. 您会发现此代码比您的代码紧凑得多,并使用了一些es6功能,这些功能实际上充分利用了react&jsx,因为您可以通过在功能组件中使用props的分解来开始工作(这很令人高兴) )以及letconst关键字的用法。

I know it doesn't directly answer your question, but I just wanted to show you that you do not need this enormous code duplication, just past styles as props (or you will have 20+ components for 20+ questions in the future, which is code you never want to maintain) 我知道它并不能直接回答您的问题,但我只是想向您展示您不需要这种巨大的代码重复,而只是将样式用作道具(否则您将来将有20多个组件来解决20多个问题,是您永远不想维护的代码)

Jsx & React set high on reusability, if you do think twice, think what you can do to unify your implementation Jsx&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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM