为什么要重置setState?

时间:2019-06-13 15:06:21

标签: reactjs state

我正在一个社区站点上,用户可以在该站点上共享和评论网站(在这里)[https://beta.getkelvin.com/]。可以按类别过滤网站,但是当用户进入特定页面然后退出时,过滤器将被删除。

这是分步进行的过程:

  • 用户从下拉列表中选择过滤器,this.state.topicSelected反映新值
  • 用户单击链接以查看实例的显示页面(网站的摘要),this.state.topicSelected反映正确的值
  • 用户返回主页,this.state.topicSelected恢复为0

我希望值仍然适用,而不是恢复为0,因此过滤器仍与用户之前选择的类别相同。

问题似乎是getInitialState正在将this.state.topicSelected的值重新设置为0(如代码中所写)。尝试将动态值放在0的位置时,出现未定义的错误。

这是getInitialState代码:

        var SitesArea = React.createClass({
            getInitialState: function () {
                return {
                    sortSelected: "most-recent",
                    topicSelected: 0 // need to overwrite with value of this.state.topicSelected when user selects filter
// I removed other attributes to clean it up for your sake
                }
            }

这是事件:

            onTopicClick: function (e) {
                e.preventDefault();
                this.setState({topicSelected: Number(e.target.value)});
                if (Number(e.target.value) == 0) {
                    if (this.state.sortSelected == 'highest-score') {
                        this.setState({sites: []}, function () {
                            this.setState({sites: this.state.score});
                        });
                    } else if (this.state.sortSelected == 'most-remarked') {
                        this.setState({sites: []}, function () {
                            this.setState({sites: this.state.remarkable});
                        });
                    } else if (this.state.sortSelected == 'most-visited') {
                        this.setState({sites: []}, function () {
                            this.setState({sites: this.state.visited});
                        });
                    } else if (this.state.sortSelected == 'most-recent') {
                        this.setState({sites: []}, function () {
                            this.setState({sites: this.state.recent});
                        });
                    }
                } else {
                    this.getSites(this.state.sortSelected, Number(e.target.value));
                    this.setState({sites: []}, function () {
                        this.setState({sites: this.state.filter_sites});
                    });
                }

最后是下拉菜单:

<select
  value={this.state.topicSelected}
  onChange={this.onTopicClick}
  className="sort"
  data-element>
    {
// Add this option in the .then() when populating siteCategories()
 [<option key='0'value='0'>Topics</option>].concat(
this.state.topics.map(function (topic) {
       return (<option
       key={topic.id}
       value={topic.id}>{topic.name}</option>);
  }))
}

如何获取该信息,以便在用户返回主页时不会重置this.state.topicSelected

1 个答案:

答案 0 :(得分:1)

我认为当用户从主页导航到摘要页面时,您的主页将被卸载(销毁)。当它们返回时,React为主页组件创建一个全新的实例。重构将所选主题初始化回0。

Here is a codepen显示可能发生的情况。 Foo ==您的主页,Bar ==摘要页面。单击几次递增主题,然后从Foo导航到Bar,然后再次返回。控制台日志显示,Foo组件在您离开时会被卸载,并在返回时进行重构。

注意,您似乎正在使用旧版本的react,如getInitialStateReact.createClass的存在所证明。我的笔遵循了在类构造函数中初始化状态的更现代方法。

要解决此问题,您必须将该状态保存在主要组件之外的某些内容中,这些内容在导航时不会被删除或重新创建。这是一些这样做的选择

  • 在您的主页上显示onTopicSelected事件。主页的父级将传入函数处理程序,作为挂钩该事件的道具。处理程序应将所选主题保存在父级的组件状态中。这是一种混乱的解决方案,因为父母通常不应该了解或关心子女的内部状态
  • 将所选主题塞入没有反应的内容,例如window.props。这也是一个丑陋的主意。
  • 了解有关redux并将其插入您的应用程序的信息。

Redux是存储此状态的最干净的方法,但这需要一些学习。如果您想看看它的外观,我已经在this codepen中实现了第一个解决方案。

显示该问题的原始Codepen作为摘要粘贴在下面。如果尝试在此处运行,请切换到整页模式。

//jshint esnext:true

const Header = (props) => {
    const {selectedPage, onNavigationChange} = props;
    const disableFoo = selectedPage == 'foo'
    const disableBar = selectedPage == 'bar';

    return (
        <div>
            <h1>Header Component : {selectedPage}</h1>
            <button disabled={disableFoo} onClick={() => onNavigationChange('foo')}>Foo page</button>
            <button disabled={disableBar} onClick={() => onNavigationChange('bar')}>Bar page</button>
            <hr/>
        </div>
    );
};

class Foo extends React.Component {
    constructor(props) {
        console.log('Foo constructor');
        super(props);
        this.state = {
            topicSelected: 0
        };
        this.incrementTopic = this.incrementTopic.bind(this);
    }


    incrementTopic() {
        const {topicSelected} = this.state
        const newTopic = topicSelected + 1
        console.log(`incrementing topic: old=${topicSelected} new=${newTopic}`)
        this.setState({
            topicSelected: newTopic
        })
    }

    render() {
        console.log('Foo::render');
        return (<div>
            <h2>The Foo content page : topicSelected={this.state.topicSelected}</h2>
                <button onClick={this.incrementTopic}>Increment topic</button>
            </div>
        );
    }

    componentWillMount() {
        console.log('Foo::componentWillMount');
    }

    componentDidMount() {
        console.log('Foo::componentDidMount');
    }

    componentWillUnmount() {
        console.log('Foo::componentWillUnmount');
    }

    componentWillUpdate() {
        console.log('Foo::componentWillUpdate');
    }

    componentDidUpdate() {
        console.log('Foo::componentDidUpdate');
    }
}


const Bar = (props) => {
    console.log('Bar::render');
    return <h2>The Bar content page</h2>
}

const Body = (props) => {
    console.log('Body::render');

    const {selectedPage} = props;

    if (selectedPage == 'foo') {
        return <Foo/>;
    } else if (selectedPage == 'bar') {
        return <Bar/>
    } 
};


class Application extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedPage: 'foo'
        };
    }

    render() {
        console.log('Application::render');
        const {selectedPage} = this.state;
        const navigationChange = (nextPage) => {
            this.setState({
                selectedPage: nextPage
            })
        }
        return (
            <div>
                <Header selectedPage={selectedPage} onNavigationChange={navigationChange}/>
                <Body selectedPage={selectedPage}/>
            </div>
        );
    }
}


ReactDOM.render(<Application/>,
    document.getElementById('main')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="main"></div>