如何避免在React中使用setProps
How to avoid using setProps in React?
我正在使用react & redux
开发应用程序,我需要setProps,但它已被弃用。
看看下面的错误:
警告:不赞成使用setProp(…)和replaceProps(…)。相反在顶层再次调用render。
未捕获的不变冲突:setProps(…):您在上调用了
setProps
具有父级的组件。这是一个反模式,因为道具会在渲染时进行反应性更新。相反,请更改所有者的render
方法将正确的值作为道具传递给组件它是在哪里创建的。
那么我该如何设置道具呢?基本上是管理一些选项卡,看看下面的代码:
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;
}
}
您似乎在没有牢牢掌握React的情况下就开始使用Redux。我不建议你这么做,因为你现在似乎有点困惑。
React中的思考是一个很好的指南,我建议您在使用Redux之前,先了解一下React中国家所有权的概念。
在React中,随时间变化的事物("状态")总是由某个组件"拥有"。有时,使用此状态进行渲染的组件是同一组件。有时,许多组件需要同步,这样状态就会被"提升"到可以管理所有组件的某个父组件,并通过道具传递信息。每当状态发生变化时,它们都会更新。
这里的重要部分是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>
);
}
});
无论哪种方法是好是坏,它们用于不同的目的。当状态与应用程序的其他部分无关时,第一个更方便,当其他远程组件碰巧也需要它时,第二个更方便。确保您了解这两种方法的权衡。
即使使用第二种方法,也必须有的东西来保持状态。它可以是另一个组件,或者(这就是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 })
}
}
在你的减速器中,你可以处理这个动作:
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
。您可以:
- 让组件拥有状态并使用CCD_ 15
- 让它接受
activeTabId
和onChange
道具,并从树中使用setState
的更高组件管理它们,或者 - 您可以将状态处理完全从组件转移到类似Redux的东西中,但您的组件仍然接受
activeTabId
和onChange
作为道具
从Container向Timeline组件提供onTabChange回调。
集装箱:
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>
);
}
});
表示组件(如Timeline组件)通常不应该管理应用程序状态。道具必须接收的所有数据和回调。
我建议您阅读有关Presentational和Container组件的文章:https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.sijqpzk93
- React重新渲染但未显示正确的组件
- React组件等待所需道具进行渲染
- React中的数据集表示
- Webpack开发服务器和React服务器端渲染
- React Redux错误页面管理
- react.js中的密钥绑定
- 如何在react js中将值从一个组件发送到另一个组件
- React Native通过Navigator将用户输入数据传递到选项卡栏IOS中的组件
- React:按键的性能提升
- 如何在react js中从一个页面导航到另一个页面
- React组件-设置页面标题
- 为react组件传递道具的最佳方式
- 如何使用带有Preact的React路由器
- 使用Jest测试React Native应用程序
- 为什么我的点击事件没有使用 react js 触发
- React 15使用空字符串删除输入值
- react组件中的绑定方法
- 如何避免在React中使用setProps
- React's setProps()已弃用警告
- React.js中已弃用的setProps()有什么替代方法?