【问题标题】:react-redux: understanding communication between Provider and representational componentsreact-redux:理解 Provider 和具象组件之间的通信
【发布时间】:2018-05-31 03:29:32
【问题描述】:

我很难理解应该如何使用我的 react 组件中的操作来分派全局商店。我对整个概念很陌生,我没有让我的组件在 dispatch() 上重新渲染。我投入很大,发现虽然 reducer 返回更新后的全局状态,但值并没有映射回组件 props。但是定义了一个适当的函数(mapStateToProps)。

最小示例:Please have a look at this plunkr(或下面的最小示例代码)。

说明: 我有一个组件Controls 和一个方法switchActivities。该组件已连接到全局存储,并且我的全局状态在组件道具中可用。

var PullFromStoreControls = function (state) {

  return {
    concrete: state.me.bool,
    nested:   state.me.nested.later
  }

}

var PushToStoreControls = function (dispatch) {
  return {
    switchFilter: function (type, value) {
      dispatch({
        type: 'SET_VAL',
        value: value
      })
    }
  }
}

Controls = connect(
  PullFromStoreControls, 
  PushToStoreControls
)(Controls)

我将变量 state.me.bool 连接到 props.conrete 以避免深度状态树的副作用。我还连接了一个调度程序以通过减速器更新全局状态。但是,如果调度程序由'switchActivities'调用,则复选框的新值使其正确地传递给reducer,然后丢失。全局状态似乎从未正确更新。

我错过了什么?

index.html

<!DOCTYPE html>
<html>

<head>

  <script data-require="react@*" data-semver="15.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.0/react.min.js"></script>
  <script data-require="react@*" data-semver="15.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.5.0/react-dom.min.js"></script>
  <script data-require="redux@*" data-semver="3.2.1" src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.2.1/redux.js"></script>
  <script data-require="react-redux@*" data-semver="4.4.5" src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.4.5/react-redux.js"></script>

  <!-- support for jsx on my localhost, on Plunkr jsx will be automatically transpiled to js -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <script type = "text/babel"  src="minimal.jsx"></script>

</head>

<body>

<div id="app"></div>

</body>

</html>

minimal.jsx

 function d(x){
  console.log(x);
}

const AppState = {
  me: {
    bool: false,
    nested: {
      later: "I also want to change values deeper in the tree."
    }
  }
}

function reducer(state, action) {

  if (state === undefined) {
    return AppState;
  }

  switch (action.type) {

    case 'SET_VAL':
      state.me.bool = action.value;
      break;

  }

  console.log("The reducer returns the changed state");
  console.log(state);

  return state;

}

// create global store with initial configuration `AppState`
const store = Redux.createStore(reducer, AppState);

// create provider and connect function not having webpack available
var Provider = ReactRedux.Provider;
var connect  = ReactRedux.connect;


class Controls extends React.Component {

  switchActivities() {

    console.log("------------------ clicked ------------------");

    console.log("set value from:");
    console.log(this.props.concrete);

    // inverse current state
    const state = !this.props.concrete;

    // output
    console.log("to:");
    console.log(state);

    // call dispatcher
    this.props.switchFilter("show_act", state);

  }

  render() {

    console.log("I would like to re-render if this.props.concrete has updated!");

    const switchActivities = <MapSwitch name="switch_act" label="Show something" checked={this.props.concrete} onChange = {() => this.switchActivities()} />;
    return <div id="map-controls">

      {switchActivities}

    </div>

  }

}

var PullFromStoreControls = function (state) {

  return {
    concrete: state.me.bool,
    nested:   state.me.nested.later
  }

}

var PushToStoreControls = function (dispatch) {
  return {
    switchFilter: function (type, value) {
      dispatch({
        type: 'SET_VAL',
        value: value
      })
    }
  }
}

Controls = connect(PullFromStoreControls, PushToStoreControls)(Controls)


const MapSwitch = ({name, label, checked, onChange}) => (

  <label for={name}>{label}

    <input type="checkbox" className="switch" data-toggle="switch"
           name={name}
           onChange={onChange}
           checked={checked}
    />

  </label>

)


ReactDOM.render(
  <Provider store={store}>
    <Controls/>
  </Provider>,
  document.getElementById('app')
);

解决方案(更新)

我在 reducer 中更改 state 对象并返回它,或者我创建一个新对象并返回它,这不同的。尽管两个返回的对象都是相同的,但前者是一个引用,而后者是一个真正的新变量。我很难学到这一点。

很好的解释: https://github.com/reactjs/redux/blob/master/docs/recipes/reducers/ImmutableUpdatePatterns.md

function reducer(state, action) {

  switch (action.type) {

    case 'SET_VAL':
      return {
        ...state,
        me : {
          ...state.me,
          bool: action.value
        }
      }
  }

  return state;

}

【问题讨论】:

    标签: javascript reactjs redux react-redux jsx


    【解决方案1】:

    你的问题是你正在变异state。 Redux 的第二个原则是 state 永远不应该被直接改变 - 相反,你的 reducer 是一个 纯函数 它应该 return 一个新的状态:https://redux.js.org/docs/introduction/ThreePrinciples.html#changes-are-made-with-pure-functions

    您的问题在这里:

    switch (action.type) {
    
        case 'SET_VAL':
          // you are attempting to mutate state.me.bool - this is an antipattern!
          state.me.bool = action.value;
          break;
    
    }
    

    相反,以返回state 的新副本的方式编写您的reducer。

    function reducer(state, action) {
        switch (action.type) {
            case 'SET_VAL':
                return {
                  ...state,
                  me : {
                    ...state.me,
                    bool: action.value
                 }
               };
             default:
                return state;
        }
    }
    

    请注意,您需要为嵌套结构复制state 的每一层。我在这里使用 Object 扩展运算符,但 Object.assign() 可以完成所有工作。希望这会有所帮助!

    【讨论】:

    • 我读了很多次“变异状态”,但不知道如何真正防止这种情况发生。在文档中Remember to return new state objects, instead of mutating the previous state. 非常感谢,先生。你能写出你的...吗?我太笨了,看不懂……
    • 好的,... 是一种 jsx 语法;)但是你的符号给了我语法错误。
    • ... 并非特定于 JSX。这是 ES6 的一个特性:redux.js.org/docs/recipes/UsingObjectSpreadOperator.html 你收到的错误是什么?如果您没有 ES6 对 Object spread 的支持,... 将不起作用。
    • 好的,多亏了你,我才开始工作。但是您的代码包含语法错误。请稍微更正您的答案(请参阅我的更新)。
    • @agoldev 我修正了语法。如果这对您有用,介意标记为正确吗?
    猜你喜欢
    • 2017-09-12
    • 2015-09-18
    • 1970-01-01
    • 2021-02-02
    • 2017-11-08
    • 1970-01-01
    • 2019-11-17
    • 2018-07-03
    • 1970-01-01
    相关资源
    最近更新 更多