看看下面的错误:
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作为道具.