我正在使用react & redux
处理应用程序,我需要setProps但不推荐使用它。
看看下面的错误:
警告:不推荐使用setProps(...)和replaceProps(...)。代替, 在顶层再次调用渲染。
未捕获的不变违规:setProps(...):您打开了
setProps
具有父级的组件。这是一种反模式,因为道具会 渲染时得到反应性更新。相反,改变所有者的render
方法将正确的值作为props传递给组件 在哪里创建。
那么如何设置道具?基本上是管理一些标签,看看我的代码:
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;
}
}
答案 0 :(得分:45)
看起来你在没有先掌握React的情况下潜入Redux。我不建议这样做,因为你现在看起来有些困惑。
Thinking in React是一个很好的指南,我建议您先使用它,然后在使用Redux之前熟悉React中的状态所有权。
在React中,随时间变化的事物(“状态”)总是由某个组件“拥有”。有时它是使用此状态进行渲染的相同组件。有时需要同步许多组件,因此状态被“提升”到可以管理它们的某个父组件,并通过props传递该信息。每当状态发生变化时,它们都会得到更新。
这里的重要部分是props
意味着由父母接收。如果组件想要更改自己的道具,则表明存在问题:
state
作为此组件,因此您可以致电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>
组件的模式,并将状态提升得更高。然后,您的组件需要接受onChange
道具,它将传递给<Tabs>
。现在它不知道如何状态更新,并且不保持状态:
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>
);
}
});
这两种方法都不是更好或更差,它们用于不同的目的。当状态与app的其余部分无关时,第一个更方便,当其他远程组件碰巧也需要它时,第二个是方便的。确保你理解两种方法的权衡。
即使采用第二种方法,某些东西也必须保持状态。它可能是另一个组件,或者(这是Redux的用武之地)它可能是一个单独的数据存储,如Redux商店。在这种情况下,不是从父组件提供onChange
,而是使用connect()
绑定它注入的onChange
prop来调度Redux操作:
const mapStateToProps = (state) => {
return {
activeTabId: state.activeTabId
}
}
const mapDispatchToProps = (dispatch) => {
return {
onChange: (tabId) => dispatch({ type: 'SET_ACTIVE_TAB', tabId })
}
}
在你的减速器中,你可以处理这个动作:
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
或activeTabId
和onChange
作为道具。答案 1 :(得分:2)
从Container提供onTabChange回调到Timeline组件。
容器:
const dispatchToProps = function(dispatch) {
return {
onClick: () => {console.log('timeline was clicked')},
onTabChange: (tabId) => { setActiveTabAction(tabId) }
}
}
const stateToProps = function(state) {
return {
timeline: state.timelineReducer.timeline,
activeTab: state.timelineReducer.activeTab
}
}
时间轴组件:
export default React.createClass({
render: function() {
return (
<div className="demo-tabs">
<Tabs activeTab={this.props.activeTab}
onChange={this.props.onTabChange} 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>
);
}
});
演示组件(如时间轴组件)通常不应管理应用程序状态。他们必须通过道具收到的所有数据和回调。
我建议你阅读有关演示文稿和容器组件的文章: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.sijqpzk93