如何避免在React中使用setProps?

gon*_*250 10 javascript reactjs redux

我正在使用应用程序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>
     );
  }
});
Run Code Online (Sandbox Code Playgroud)

容器

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)
Run Code Online (Sandbox Code Playgroud)

减速器

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*mov 45

看起来你在没有首先牢牢抓住React的情况下潜入Redux.我不建议这样做因为你现在看起来有些困惑.

在React中思考是一个很好的指南,我建议你先使用它,然后在使用Redux之前熟悉React中的状态所有权.

在React中,随时间变化的事物("状态")总是由某个组件"拥有".有时它是使用此状态进行渲染的相同组件.有时需要同步许多组件,因此状态被"提升"到可以管理它们的某个父组件,并通过props传递该信息.每当状态发生变化时,它们都会得到更新.

这里的重要部分是props父母要接受的.如果组件想要更改自己的道具,则表明存在问题:

  1. 你应该使用state这个组件,所以你可以打电话setState
  2. 或者您的组件需要访问回调道具,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>
     );
  }
});
Run Code Online (Sandbox Code Playgroud)

但是,您可以继续使用<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>
     );
  }
});
Run Code Online (Sandbox Code Playgroud)

这两种方法都不是更好或更糟,它们用于不同的目的.当状态与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 })
  }
}
Run Code Online (Sandbox Code Playgroud)

在您的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)
Run Code Online (Sandbox Code Playgroud)

在两种情况下,我们需要的setProps.你可以:

  • 让组件拥有状态和使用setState,
  • 让它接受activeTabIdonChange使用树中较高的组件来管理和管理它们setState,或者
  • 你可以将状态处理完全从组件中移到像Redux这样的东西,但是你的组件仍然会接受activeTabIdonChange作为道具.