简体   繁体   English

调度动作时未调用redux connect mapStateToProps

[英]redux connect mapStateToProps not called when action is dispatched

Solution(updated): 解决方案(更新):

I thought any action would cause react-redux-connect to call the mapState functions but when an action doesn't change anything then this is not the case. 我以为任何动作都会导致react-redux-connect调用mapState函数,但是当一个动作什么都没有改变时,事实并非如此。

I have a localStorage module that dispatches actions but don't change state, instead thy will write to localStorage. 我有一个localStorage模块,该模块可以调度操作,但不更改状态,相反,您会写入localStorage。 The module has selectors that are used in the containers but they won't get called until the state actually changes so the UI would only show correctly after another action was dispatched that would change the state. 该模块具有在容器中使用的选择器,但是直到状态实际更改时它们才被调用,因此,只有在分派了另一个更改状态的操作之后,UI才能正确显示。

Problem 问题

When I put the store on window ( window.store=store ), add a console.log in the mapStateToProps, then in the console I dispatch an action: store.dispatch({type:'some action'}) then the console.log of the mapStateToProps does not show. 当我将商店放在窗口( window.store=store )上时,在mapStateToProps中添加console.log,然后在控制台中分派一个动作: store.dispatch({type:'some action'})然后是控制台。 mapStateToProps的日志不显示。

I do memoize the result but the mapStateToProps should be called see here 我确实记住了结果,但是应该调用mapStateToProps, 请参见此处

Full code is here and running example here (you can open a console clicking on 'console' link in the right bottom of the screen). 完整的代码在这里 ,运行示例在这里 (您可以单击屏幕右下角的“控制台”链接打开控制台)。

package.json 的package.json

store.js: store.js:

import { createStore } from 'redux';
export default (initialState, reducer) => {
  const store = createStore(
    reducer,
    initialState,
    window.__REDUX_DEVTOOLS_EXTENSION__ &&
      window.__REDUX_DEVTOOLS_EXTENSION__()
  );
  window.store = store;
  return store;
};

app.js app.js

import React from 'react';
import { connect } from 'react-redux';
import './App.css';
import createStore from './store';
import { Provider } from 'react-redux';
import initCounter from './components/Counter';
import {
  createWrapper,
  memoize,
} from './components/@common';
const COUNTER = 'COUNTER';
const selectCounterState = state => state.myCounter;
const counter = initCounter({
  actionWrapper: createWrapper(COUNTER, 'counter1'),
  selectors: { myState: selectCounterState },
  connect,
  memoize,
});
const initialState = {
  myCounter: counter.initialState,
};
const reducer = (state = initialState, action) => {
  if (action.emittedBy === COUNTER) {
    return {
      ...state,
      myCounter: counter.reducer(
        selectCounterState(state),
        action.payload
      ),
    };
  }
  return state;
};
const store = createStore(initialState, reducer);
const Counter = counter.container;
const App = () => (
  <Provider store={store}>
    <Counter id="counter1" parentId={[]} />
  </Provider>
);

export default App;

component/Counter/index: 组件/计数器/索引:

import component from './component';
const INCREASE = 'INCREASE';
const reducer = (state, action) => {
  if (action.type === INCREASE) {
    return { ...state, count: state.count + 1 };
  }
  return state;
};
const makeState = memoize =>
  memoize((id, parentId, { count }) => ({
    id: parentId.concat(id),
    parentId,
    count,
  }));
const mapStateToProps = ({ myState }, memoize) => () => {
  const newState = makeState(memoize);
  return (state, ownProps) =>
    console.log('in map state to props', new Date()) ||
    newState(
      ownProps.id,
      ownProps.parentId,
      myState(state)
    );
};

export default ({
  actionWrapper,
  selectors,
  connect,
  memoize,
}) => {
  const actions = {
    increase: ({ id }) =>
      actionWrapper({
        type: INCREASE,
        id,
      }),
  };
  const container = connect(
    mapStateToProps(selectors, memoize),
    actions
  )(component);
  return {
    container,
    reducer,
    initialState: { count: 0 },
  };
};

components/counter/component.js: 组件/计数器/ component.js:

import React from 'react';

export default props => (
  <div>
    <button onClick={() => props.increase(props)}>
      add
    </button>
    {props.count}
  </div>
);

Your example codepen works just fine, you just have to trigger an action that gets past your top level guard and is of the expected structure, as to not cause any followup errors: 您的示例代码笔工作得很好,您只需触发一个超出顶级警卫并且具有预期结构的操作即可,以免引起任何后续错误:

Post this into the console of your codepen: 将其发布到您的Codepen控制台中:

store.dispatch({emittedBy: "COUNTER", type: "COUNTER -> INCREASE", id: "counter1", payload: {type: "INCREASE", id: ["counter1"]}})

This problem was caused because I had a localStorage module that did dispatch actions but did not change the state, instead it would write to localStorage. 造成此问题的原因是,我有一个localStorage模块,该模块执行调度操作,但未更改状态,而是将其写入localStorage。

The module had selectors that would get the right data and the containers would use them to build the correct state but since the dispatched action did not change the state in the redux store react-redux would skip calling my mapState functions (probably memoizing state in Provider). 该模块具有选择器,这些选择器将获取正确的数据,并且容器将使用它们来构建正确的状态,但是由于分派的操作并未更改redux存储中的状态,因此react-redux会跳过调用我的mapState函数(可能在Provider中记住状态) )。

The solution is to let the root reducer return a new state reference {...state} so any action would cause the mapState functions to be called. 解决方案是让根缩减器返回新的状态引用{...state}以便任何操作都会导致mapState函数被调用。

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

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