[英]Render different child components in React based on state
我在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
,我是否能夠將它們的樣式設置為彼此不同?
對於遇到此問題的任何人:我犯了一個非常愚蠢的錯誤,並且在我的狀態下,我的answers
對象中缺少一些逗號。 當我解決此問題后,一切運行正常。
在您當前的問題中,事情可以用不同的方式編寫,例如,您的onSelect
方法現在是一個巨大的switch語句,盡管您的原型和狀態屬性似乎匹配,但是可以代替以下巨大的開關來代替這個巨大的開關:
onSelect(value) {
this.setState(prevState => {
return {
[value]: prevState[value] + 1,
counter: prevState.counter + 1
}
});
}
這將做完全相同的事情,因為原型可以完美地匹配您的屬性,並且您在每一步中都會增加計數器,因此無需多次進行操作;)
這甚至也適用於您的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 });
}
從長遠來看,這確實減少了很多代碼重復,並且將簡化您的原因,但是在我的答案的原始部分,我還想給您提供一種不同的方式來構建所有內容;)
我主要想為您提供一種不同的代碼結構方式。 抱歉,我不得不重新創建RadioButton
& RadioGroup
new,但是我沒有一次找到正確的CDN(因此請忽略那些實現;))
主要是,此應用程序還會為您提供一個小測驗,它可以跟蹤正確的答案以及給出的答案(但是,不會跟蹤正確的答案;)
您會發現此代碼比您的代碼緊湊得多,並使用了一些es6功能,這些功能實際上充分利用了react&jsx,因為您可以通過在功能組件中使用props的分解來開始工作(這很令人高興) )以及let
, const
關鍵字的用法。
我知道它並不能直接回答您的問題,但我只是想向您展示您不需要這種巨大的代碼重復,而只是將樣式用作道具(否則您將來將有20多個組件來解決20多個問題,是您永遠不想維護的代碼)
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.