【问题标题】:Redux reducer not updating prop value in React component from dispatch outside React componentsRedux reducer 不会从 React 组件外部调度更新 React 组件中的 prop 值
【发布时间】:2018-06-05 14:23:17
【问题描述】:

当动作被分派到映射到该组件的减速器时,我在更新我的 react 组件上的 props 属性时遇到问题。我将在显示代码的过程中进行解释。

首先,我试图在我的 react 组件之外调度操作(从 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
    };
};

一旦 action 被调度,它就会被这个 reducer 拾取。请注意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;
    }
};

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

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,这是最初传递给接收该消息的减速器的默认值(尽管上面我们知道控制台。当从菜单服务分派操作时,登录减速器确实打印了正确的值。

这也是我要添加到商店的减速器:

import { combineReducers } from 'redux';

import FileExplorerReducer from './file-explorer';

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

export default allReducers;

在此问题上的任何帮助将不胜感激。我相信此时我倾向于它是 StoreProvider 的一个问题,并且可能有 2 个单独的商店实例?不太清楚为什么组件 this.props.fileExplorer 始终为 null,即使 reducer 返回 action.payload 并且 store 的 fileExplorer 状态已更新(至少在调度后检查菜单服务)。

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

因此必须允许 IPC(进程间通信),我们必须像这样在菜单服务中从 BrowserWindow.webContents.send() 发布消息:

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

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

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());
    }
}

【问题讨论】:

  • 尝试控制台在您的 Sidemenu 组件 mapstatetoprops 中记录您的状态,以确保您拥有正确的状态
  • @cdoshi 就是这么做的,哈哈。它给了我一个属性为“fileExplorer:null”的对象。肯定认为商店发生了一些有趣的事情
  • 还只是在使用随机数创建商店时为 fileExplorer 添加了一个默认值,在 Provider 中加载商店时的实例上,我得到了 52,然后当我得到了store in the menu service 我得到了 75。所以确认有两个单独的 store 实例,只需要弄清楚如何将它保持在一个。以为我正在实现一个单例来使用。
  • 从您上面的代码来看,您似乎没有多个商店。很抱歉在这里没有太大帮助
  • 各位好人,感谢您抽出宝贵时间观看!

标签: javascript reactjs typescript redux react-redux


【解决方案1】:

我不能完全确定,但根据您说您正在尝试执行 Electron 的菜单事件的描述,我建议您检查您正在调度菜单事件的进程。如果它不是托管反应组件的渲染器进程,您可能会看到每个不同的进程都有独立的存储状态,可能需要用于 redux 的存储同步中间件,或者将 IPC 调用到渲染器进程中,然后让渲染器直接为渲染器端的 redux 存储调度操作。

【讨论】:

    【解决方案2】:

    我在非反应组件中调用store.dispatch() 时也面临同样的问题。我不确定它是否正确,但我可以通过将存储传递给组件并从组件传递接收到的存储到非反应组件,然后使用这个接收到的商店来调度我的动作。我在下面添加了几个 sn-p

    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 }

    【讨论】:

      猜你喜欢
      • 2021-11-15
      • 2017-12-20
      • 1970-01-01
      • 2021-05-29
      • 1970-01-01
      • 2016-06-30
      • 2018-05-16
      • 2021-04-09
      • 2018-04-08
      相关资源
      最近更新 更多