Redux reducer不会从React组件外部的调度更新React组件中的prop值

时间:2017-12-23 04:49:21

标签: javascript reactjs typescript redux react-redux

当将动作调度到映射到该组件的reducer时,我遇到了更新我的react组件上的props属性的问题。我将在显示代码的过程中进行解释。

首先,我试图在我的反应组件之外调度动作(从Electron菜单项点击发出的调度)。

这是调度操作的菜单服务功能。如果我们在发送后执行console.log(store.getState()),我们会在商店状态中看到正确的消息:

dispatchMessage(): void {
    let message = messageService.getMessage();
    let store = StoreProvider.getInstance().store;
    store.dispatch(loadMessage(message));
}

注意StoreProvider获取Redux存储的实例,这是因为代码超出了React组件的范围,这就是StoreProvider.ts:

import { createStore } from 'redux';

import allReducers from '../reducers/index';

export default class StoreProvider {
    private static _instance: StoreProvider;
    store: any = null;

    constructor() {
        this.store = createStore(
            allReducers
        );
    }

    public static getInstance(): StoreProvider {
        return StoreProvider._instance || (StoreProvider._instance = new StoreProvider());
    }
}

这是我的index.tsx文件,我在其中引导App组件并使用相同的商店提供程序加载商店(这将填充StoreProvider的新实例,其中构造函数将设置我稍后可以访问的商店菜单项点击):

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import allReducers from './reducers/index';
import App from './app';
import StoreProvider from './providers/store-provider';

const bootstrapperElement: HTMLElement = document.getElementById('app') as HTMLElement;

const store = StoreProvider.getInstance().store;

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    bootstrapperElement);

好的,这就是事情如何连接到调度动作的位置。因此,当我在菜单服务功能中调度我的动作时,它正在调用此动作:

export const loadMessage = (message: any) => {
    return {
        type: 'MESSAGE_LOADED',
        payload: message
    };
};

一旦调度动作,它就被这个减速器拾取。注意console.log(action.payload),此时它将正确记录从菜单服务调度的消息,我可以看到预期的内容,到目前为止似乎正在工作。

export default (state: any = null, action: any) => {
    switch (action.type) {
    case 'MESSAGE_LOADED':
        console.log(action.payload);
        return action.payload;
    default:
        return state;
    }
};

这是将该状态映射到props的组件:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

class SideMenu extends Component<any> {
    render() {
        return (
            <div>
                <h2 onClick={ () => console.log(this.props.fileExplorer)}>Test</h2>
            </div>
        );
    }
}

const mapStateToProps = (state: any) => {
    return {
        fileExplorer: state.fileExplorer
    };
};

export default connect<any>(mapStateToProps, {})(SideMenu);

所以当我单击带有'TEST'文本的h2元素时,我总是得到null,这是最初传递给正在拾取该消息的reducer的默认值(尽管我们知道控制台。当从菜单服务调度动作时,登录reducer确实打印了正确的值。

这也是我正在添加到商店的减速机:

import { combineReducers } from 'redux';

import FileExplorerReducer from './file-explorer';

const allReducers = combineReducers({
    fileExplorer: FileExplorerReducer
});

export default allReducers;

我们将非常感谢您对此问题的任何帮助。我相信在这一刻我倾向于它是StoreProvider的一个问题,可能有两个单独的商店实例?不太清楚为什么组件this.props.fileExplorer总是为null,即使reducer返回action.payload并且更新了商店的fileExplorer状态(至少在发送后检查菜单服务)。

根据接受的答案更改回答上述问题:

因此必须允许IPC(进程间通信),我们必须在菜单服务中发布来自BrowserWindow.webContents.send()的消息,如下所示:

let message = let message = messageService.getMessage();
BrowserWindow.getFocusedWindow().webContents.send('dispatch-action', { payload: { message});

然后在Store Provider中,我在构造函数中添加了订阅者,因此商店的实例可以接收在渲染器进程中从主进程发布的消息,如下所示:

import { ipcRenderer } from 'electron';

export default class StoreProvider {
    private static _instance: StoreProvider;
    store: any = null;

    constructor() {
        this.store = createStore(
            allReducers
        );

        ipcRenderer.on('dispatch-action', (event: any, arg: any) => {
            this.store.dispatch(loadMessage(arg.payload));
        });
    }

    public static getInstance(): StoreProvider {
        return StoreProvider._instance || (StoreProvider._instance = new StoreProvider());
    }
}

2 个答案:

答案 0 :(得分:1)

我不能完全肯定,但根据你的描述说你试图在Electron的菜单事件上执行,我想建议检查你正在调度菜单事件的过程。如果它不是渲染您的react组件的渲染器进程,您可能会看到每个不同的进程都有隔离的存储状态,可能需要存储用于redux的同步中间件或者调用IPC到渲染器进程,然后让渲染器直接为渲染器端redux存储调度操作。 / p>

答案 1 :(得分:0)

在非反应组件中调用store.dispatch()时,我也面临着同样的问题。我不确定它是否正确,但是我可以通过将存储传递到组件以及从组件传递到接收的组件来修复它存储到非React组件,然后使用收到的存储来调度我的操作。我在下面添加了一些代码段

index.js

<Provider store={configstore}>
    <BrowserRouter>
        <App>
            <Switch>

                <Route exact path="/addinvoice" component={authGuard(AddInvoice)} storeHack={configStore} />
            </Switch>
        </App>
    </BrowserRouter>
</Provider>

添加发票

class AddInvoice extends Component {
constructor(props){
    super(props)
    this.state={
        store:props
    }
    console.log("store received in add invoice is",this.state) 
}
render(){
 return(
    //pass this.state.store to the non react-component
    //and use the received parameter.dispatch(actionName) in ur non-react component and u ready to go
  )

参考:got this idea from this blogs, authors repo in github     }