看看下面的错误:
Warning: setProps(…) and replaceProps(…) are deprecated. Instead,
call render again at the top level.Uncaught Invariant Violation: setProps(…): You called
setProps
on
a component with a parent. This is an anti-pattern since props will
get reactively updated when rendered. Instead,change the owner’s
render
method to pass the correct value as props to the component
where it is created.
- export default React.createClass({
- render: function() {
- return (
- <div className="demo-tabs">
- <Tabs activeTab={this.props.activeTab}
- onChange={(tabId) => this.setProps({ activeTab: tabId })} ripple>
- <Tab>Stack</Tab>
- <Tab>GitHub</Tab>
- <Tab>Twitter</Tab>
- </Tabs>
- <section>
- <div className="content">Content for the tab: {this.props.activeTab}</div>
- </section>
- </div>
- );
- }
- });
容器
- import React from 'react';
- import TimeLine from './timeline';
- import { connect } from 'react-redux';
- import { getStackoverflow } from 'api/timeline';
- const TimeLineContainer = React.createClass({
- componentWillMount: function() {
- getStackoverflow();
- },render: function() {
- return (
- <TimeLine {...this.props} />
- )
- }
- });
- const stateToProps = function(state) {
- return {
- timeline: state.timelineReducer.timeline
- }
- }
- const dispatchToProps = function(dispatch) {
- return {
- onClick: () => {console.log('timeline was clicked')}
- }
- }
- export default connect(stateToProps,dispatchToProps)(TimeLineContainer)
减速器
- var timelineInitialState = {
- github: [],gist: [],stackoverflow: [],twitter: [],activeTab: 2
- }
- export default function(state = timelineInitialState,action) {
- switch (action.type) {
- case 'GET_GITHUB':
- //TODO: implement.
- break;
- case 'GET_GIST':
- //TODO: implement.
- break;
- case 'GET_STACKOVERFLOW':
- var stackoverflowState = Object.assign({},state)
- stackoverflowState.stackoverflow = action.stackoverflow;
- return stackoverflowState;
- break;
- case 'GET_TWITTER':
- //TODO: implement.
- break;
- default:
- return state;
- }
- }
解决方法
Thinking in React是一个很好的指南,我建议您先通过它,并在使用Redux之前,在React中了解国家所有权的想法.
在反应中,随着时间的推移而变化的事物(“状态”)总是被某个组件“拥有”.有时它是使用这种状态进行渲染的组件.有时许多组件需要同步,所以状态可以“挂起”到可以管理它们的一些父组件,并通过道具传递信息.每当状态发生变化时,都会更新.
这里的重要部分是道具是由父母收到的.如果一个组件想改变自己的道具,这是一个问题的症状:
>您应该为此组件使用状态,因此可以调用setState
>或者你的组件需要访问像onChange这样的回调支持,所以它可以“询问”要更改的值
在第一种情况下,你的代码看起来像这样,它将是有效的React:
- export default React.createClass({
- getInitialState: function() {
- return { activeTab: 0 }
- },render: function() {
- return (
- <div className="demo-tabs">
- <Tabs activeTab={this.state.activeTab}
- onChange={(tabId) => this.setState({ activeTab: tabId })} ripple>
- <Tab>Stack</Tab>
- <Tab>GitHub</Tab>
- <Tab>Twitter</Tab>
- </Tabs>
- <section>
- <div className="content">Content for the tab: {this.state.activeTab}</div>
- </section>
- </div>
- );
- }
- });
但是,您可以使用< Tabs>继续使用您已使用的模式内部组件,起升状态更高.那么你的组件将需要接受它将传递给< Tabs>的onChange支持.现在它不知道状态如何更新,并且不会保持状态:
- export default React.createClass({
- render: function() {
- return (
- <div className="demo-tabs">
- <Tabs activeTab={this.props.activeTab}
- onChange={this.props.onChange} ripple>
- <Tab>Stack</Tab>
- <Tab>GitHub</Tab>
- <Tab>Twitter</Tab>
- </Tabs>
- <section>
- <div className="content">Content for the tab: {this.props.activeTab}</div>
- </section>
- </div>
- );
- }
- });
这两种方法都不是更好还是更坏,它们用于不同的目的.当状态与应用程序的其余部分无关时,第一个更方便,第二个方便当其他远程组件也需要它时.确保您了解这两种方法的权衡.
即使有了第二种方法,有些事情就要持续下去.它可能是另一个组件,或者(这就是Redux所在的地方),它可能是一个单独的数据存储,如Redux存储.在这种情况下,而不是从父组件提供onChange,您可以使用connect()绑定其注入的onChange支持来分派Redux操作:
- const mapStateToProps = (state) => {
- return {
- activeTabId: state.activeTabId
- }
- }
- const mapDispatchToProps = (dispatch) => {
- return {
- onChange: (tabId) => dispatch({ type: 'SET_ACTIVE_TAB',tabId })
- }
- }
在您的reducer中,您可以处理此操作:
- const activeTabId = (state = 0,action) => {
- switch (action.type) {
- case 'SET_ACTIVE_TAB':
- return action.tabId
- default:
- return state
- }
- }
- const reducer = combineReducers({
- activeTabId,// other reducers
- })
- const store = createStore(reducer)
在这两种情况下,我们都需要setProps.你可以:
>让组件拥有状态并使用setState,>让它接受activeTabId和onChange道具,并从树中更高层使用setState的组件进行管理,或>您可以将状态处理完全从组件移动到Redux中,但您的组件仍将接受activeTabId和onChange作为道具.