简体   繁体   English

react-redux:了解提供者和代表性组件之间的通信

[英]react-redux: understanding communication between Provider and representational components

I have difficulties understanding how the global store should be dispatched with actions from my react components. 我很难理解应如何使用我的React组件中的动作调度全局存储。 I'm very new to the whole concept and I don't get my component to re-render on dispatch(). 我对整个概念还很陌生,我没有让我的组件在dispatch()上重新渲染。 I invested deeply and found that although the reducer returns the updated global state the values are not mapped back to the component props. 我进行了深入的研究,发现虽然化简器返回了更新的全局状态,但这些值并未映射回组件prop。 But a proper function (mapStateToProps) is defined. 但是定义了适当的函数(mapStateToProps)。

Minimal example: Please have a look at this plunkr (or minimal example code below). 最小示例: 请查看此插件 (或下面的最小示例代码)。

Explanation: I have a component Controls with a method switchActivities . 说明:我有一个带有方法switchActivities Controls The component is connected to the global store and my global state is available in the component props. 该组件已连接到全局商店,并且组件属性中提供了我的全局状态。

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)

I wired the variable state.me.bool to props.conrete to avoid side-effects of a deep state tree. 我将变量state.me.boolprops.conrete以避免产生深层状态树的副作用。 I also connected a dispatcher to update the global state via the reducer. 我还连接了一个调度程序,以通过化简器更新全局状态。 However, if the dispatcher is invoked by 'switchActivities' the new value of the checkbox makes it up to the reducer correctly and then gets lost. 但是,如果调度程序由“ switchActivities”调用,则复选框的新值会使它正确归约到reducer,然后丢失。 The global state seems never updated correctly. 全局状态似乎从未正确更新。

What am I missing? 我想念什么?

index.html 的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 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')
);

Solution (update) 解决方案(更新)

It is a difference whether I alter the state object within the reducer and return that or if I create a new object and return that. 无论在化简器中更改state对象然后将其返回还是创建新对象然后将其返回都不同。 Although both returned objects are the same the former is a reference while the latter is a real new variable. 尽管两个返回的对象都相同,但前者是一个引用,而后者是一个真正的新变量。 I learned that the hard way. 我了解到这很困难。

Good explanation: https://github.com/reactjs/redux/blob/master/docs/recipes/reducers/ImmutableUpdatePatterns.md 好的解释: 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;

}

Your problem is that you are mutating state . 你的问题是你正在变异state The second principle of Redux is that the state should never be mutated directly - rather, your reducer is a pure function which should return a new state: https://redux.js.org/docs/introduction/ThreePrinciples.html#changes-are-made-with-pure-functions Redux的第二个原则是state永远不能直接改变-相反,reduce是一个纯函数 ,应该return一个新状态: https : //redux.js.org/docs/introduction/ThreePrinciples.html#changes-被制成与-纯函数

Your issue is here: 您的问题在这里:

switch (action.type) {

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

}

Instead, write your reducer in a way that returns a new copy of state . 相反,以返回state的新副本的方式编写reducer。

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

Notice that you need to copy each level of state for nested structures. 注意,您需要复制嵌套结构的每个state级别。 I'm using the Object spread operator here, but Object.assign() with all work. 我在这里使用对象散布运算符,但是所有工作都使用Object.assign()。 Hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM