react-redux:理解Provider和代表组件之间的沟通

时间:2017-12-17 17:03:23

标签: javascript reactjs redux react-redux jsx

我很难理解应该如何使用反应组件的操作调度全局存储。我对整个概念都很陌生,我不会让我的组件在dispatch()上重新渲染。我投入了很多,发现虽然reducer返回更新的全局状态,但值不会映射回组件props。但是定义了一个合适的函数(mapStateToProps)。

最小示例:pundit(或下面的最小示例代码)。

说明: 我有一个组件Controls,其方法为switchActivities。组件连接到全局存储,我的全局状态在组件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)

我将变量state.me.bool连接到 props.conrete 以避免深度状态树的副作用。我还连接了一个调度程序,通过reducer更新全局状态。但是,如果调度员是由“交换活动”调用的,那么复选框的新值使其正确到达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对象并返回该对象,或者我是否创建了一个新对象并返回该对象。虽然两个返回的对象都是相同的,但前者是引用,而后者是一个真正的新变量。我学到了很难的方法。

好的解释: Please have a look at this plunkr

function reducer(state, action) {

  switch (action.type) {

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

  return state;

}

1 个答案:

答案 0 :(得分: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 spread操作符,但Object.assign()使用了所有工作。希望这有帮助!