确保使用某个组件的新实例

时间:2016-09-19 10:02:04

标签: reactjs redux

我有一种感觉,这是一个“错误”的问题,但无论如何都要进行:

我正在制作某种测验应用程序(使用redux进行状态管理)。 (在这里显示重要的部分)

quiz.js

<Slider {...sliderSettings} slideIndex={currentQuestionIndex}>
    <Start onStart={() => onNextQuestion()} topicId={topicId}  />

    {
        questions.map((question, ndx) => {
            return (
                <Question {...question} done={onDone} key={`question-${question.id}`} />
            );
        })
    }

    <Result score={score} onRestart={() => onRestart()}/>
</Slider>

question.js

<div className="question">
    <h2 className="question__text">{ question }</h2>
    <MultipleChoice options={answers} onChange={done} />
</div>

多choice.js

const
    initialState = {
        selectedValue: null
    };

class MultipleChoice extends Component {
    constructor(props) {
        super(props);
        this.state = initialState;
    }

    handleChange(value, correct) {
        this.setState({
            selectedValue: value
        });

        this.props.onChange(correct);
    }

    render() {
        const
            { options } = this.props,
            getStateClass = (option, ndx) => {
                let sc = '';

                if (this.state.selectedValue !== null) {
                    if (this.state.selectedValue === ndx) {
                        sc = option.correct ? 'is-correct' : 'is-incorrect';
                    } else if (option.correct) {
                        sc = 'is-correct';
                    }
                }

                return sc;
            };

        return (
            <ul className="multiple-choice">
                { options.map((option, ndx) => {
                    return (
                        <li key={`option-${ndx}`} className={cx('multiple-choice__option', getStateClass(option, ndx))}>
                            <button className="multiple-choice__button" onClick={() => this.handleChange(ndx, option.correct)}>{option.answer}</button>
                        </li>
                    );
                }) }
            </ul>
        );
    };
}

export default MultipleChoice;

问题在于MultipleChoice的渲染。它使用内部状态来显示哪个答案是错误的。

在quiz.js中,onRestart调度一个redux动作,该动作更新存储以获取一些新问题并将currentQuestionIndex重置为0.这一切都有效。 但不知何故,有时候MultipleChoice元素被“重用”,并且仍然显示它在上一轮问题中的状态。换句话说,大多数情况下会安装新的MultipleChoice,但有时却不是。如果我理解正确,这是反应和解吗?

但我该如何解决这个问题呢?在我看来,MultipleChoice需要它的内部状态。所以我应该以某种方式重置这个状态?或者确保每次都安装一个新的MultipleChoice?或者我在这里问错了问题?

2 个答案:

答案 0 :(得分:15)

我查看了your repository,正确noted in another answer问题是您的<Question>(以及内部<MultipleChoice>)组件从未卸载,因此他们保持他们的状态。

通常情况下,这在React中并不常见,因为人们通常希望在组件位于树中时保留状态。当不再需要状态时,人们通常会停止在render()方法中包含组件,并且React会卸载它们。下次渲染时,状态会重置。

状态会在您的示例中重置,因为总是使<Question>可见,即使在测验运行之间也是如此。您可以看到<Question>已经在我们开始测验之前已经安装,并在结束后保持挂载:

stale-questions

那么我们如何强迫React重置他们的状态呢?有三种选择:

  • 您可以让它们卸载。下次他们上架时,他们会有一个新的状态。这通常是最简单的解决方案,因为您实际上并未在初始“开始测验”页面上显示问题。例如,您可以通过在currentQuestionIndex > 0 && questions.map(...)方法中呈现render()之前添加<Questions>后卫来执行此操作。

  • 您可以将新的key传递给与之前key不匹配的新question-${question.id}。您目前正在使用key作为quizAttemptIndex,但即使您重试测验,也会为同一问题生成相同的密钥。要解决此问题,您可以引入一个新的状态变量(在Redux或顶级组件状态中),例如question-${quizAttemptIndex}-${question.id},并在任何新尝试时递增它。然后,您可以将密钥更改为key。这种方式再次尝试测验会重置问题的内部状态(以及使其重新安装)。

  • 最后,如果你不想通过传递不同的quizAttemptIndex来完全破坏DOM,你可以将<MultipleChoice>(在上一节中解释)作为道具传递给{{1 }}。然后,在其中,如果this.setState({ selectedValue: null }),您可以在componentWillReceiveProps(nextProps)nextProps.quizAttemptIndex !== this.props.quizAttemptIndex

您可以选择任一解决方案,具体取决于您始终保持问题的重要性。

答案 1 :(得分:0)

据我所知,你希望你的MultipleChoice组件在你想要的时候刷新它的状态。但是,当您在组件中使用react状态时,只要组件未卸载,MultipleChoice中的反应状态就会保持其最新状态。

此行为是反应状态所期望的,因为大多数情况下您希望将其用于内部行为。也许您想在组件中切换一些ui数据,当某个按钮或某些东西触发您的组件时。或者您想控制输入表单等。

但你期望应该在redux状态下完成。您的组件应该是可重复使用的,而不关心它的安装位置。您将与装载地点相关的数据传递给带有道具的MultipleChoice,道具来自redux。所以现在你有了一个可重用的组件。不要花太多时间来安装等。你可以在github上检查react-redux存储库,以熟悉如何塑造项目的数据流。何时使用redux状态通过反应状态制作组件中的desicions或处理状态。