【问题标题】:How to avoid using setProps in React?如何避免在 React 中使用 setProps?
【发布时间】:2016-04-15 09:43:03
【问题描述】:

我正在使用 react & redux 开发应用程序,我需要 setProps,但它已被弃用。

看看下面的错误:

警告:不推荐使用 setProps(...) 和 replaceProps(...)。反而, 在顶层再次调用 render。

Uncaught Invariant Violation: setProps(...): You call setProps on 有父组件的组件。这是一种反模式,因为道具会 渲染时得到响应式更新。相反,更改所有者的 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;
  }
}

【问题讨论】:

  • 听起来你需要自己设置的道具应该是内部状态,你应该调用 setState 。或者保留道具,但让它在它改变时设置状态(同步道具和状态)
  • 就像@JuanMendes 所说,你会想要使用状态。你可以做的是使用你的道具设置你的初始状态,然后将更新后的值设置为状态
  • @realseanp 你必须确保你实现了componentWillReceiveProps,所以当父组件在初始渲染后设置一个道具时它会更新状态

标签: javascript reactjs redux


【解决方案1】:

看起来您在没有牢牢掌握 React 的情况下就开始使用 Redux。我不建议您这样做,因为您现在似乎有些困惑。

Thinking in React 是一个很好的指南,我建议您在使用 Redux 之前先通读一遍并熟悉 React 中的国家所有权概念。

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

这里的重要部分是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>
     );
  }
});

但是,您可以在内部使用 &lt;Tabs&gt; 组件继续您已经使用的模式,并将状态提升得更高。然后你的组件需要接受一个onChange prop,它将传递给&lt;Tabs&gt;。现在它不知道如何更新状态,也不持有状态:

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>
     );
  }
});

这两种方法都不是更好或更坏,它们用于不同的目的。当状态与应用程序的其余部分无关时,第一个更方便,当其他远程组件碰巧也需要它时,第二个更方便。确保您了解这两种方法的权衡。

即使使用第二种方法,something 也必须保持状态。它可能是另一个组件,或者(这就是 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。您可以:

  • 让组件拥有状态并使用setState
  • 让它接受activeTabIdonChange 属性,并从树中较高的组件中管理它们,该组件将使用setState,或者
  • 您可以将状态处理完全从组件中移到 Redux 之类的东西中,但您的组件仍将接受 activeTabIdonChange 作为道具。

【讨论】:

    【解决方案2】:

    从 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>
         );
      }
    });
    

    演示组件(如您的时间轴组件)通常不应管理应用程序状态。他们必须通过 props 接收的所有数据和回调。

    我建议您阅读有关 Presentational 和 Container 组件的文章: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.sijqpzk93

    【讨论】:

      猜你喜欢
      • 2014-09-28
      • 1970-01-01
      • 2022-01-04
      • 1970-01-01
      • 2020-01-01
      • 2017-03-22
      • 2018-06-04
      • 2020-12-21
      • 2017-05-11
      相关资源
      最近更新 更多