AspNet Core SignalR和Redux配置-发送和接收数据

问题描述 投票:0回答:1

我是使用AspNet Core SiginalR和React.js + Redux的聊天功能。

问题步骤我可以通过signalR发送消息到后端服务器我可以在中间件上收到消息但是对于存储和更新视图中的状态更新,我不愿意[[调度。

问题

我做错了什么? 也许我无法从回调访问<< connection.on(“ ReceiveMessage”,data => ...

是吗?如何解决?

App

import '@fake-db' import React, {Suspense} from 'react'; import {FuseAuthorization, FuseLayout, FuseTheme} from '@fuse'; import Provider from 'react-redux/es/components/Provider'; import {Router} from 'react-router-dom'; import jssExtend from 'jss-extend'; import history from '@history'; import {Auth} from './auth'; import store from './store'; import AppContext from './AppContext'; import routes from './fuse-configs/routesConfig'; import {create} from 'jss'; import {StylesProvider, jssPreset, createGenerateClassName} from '@material-ui/styles'; import axios from 'axios'; const jss = create({ ...jssPreset(), plugins : [...jssPreset().plugins, jssExtend()], insertionPoint: document.getElementById('jss-insertion-point'), }); axios.defaults.baseURL = 'https://localhost:5001/api/v1/'; const generateClassName = createGenerateClassName(); const App = () => { return ( <Suspense fallback="loading"> <AppContext.Provider value={{routes}}> <StylesProvider jss={jss} generateClassName={generateClassName}> <Provider store={store}> <Auth> <Router history={history}> <FuseAuthorization> <FuseTheme> <FuseLayout/> </FuseTheme> </FuseAuthorization> </Router> </Auth> </Provider> </StylesProvider> </AppContext.Provider> </Suspense> ); }; export default App;

创建商店


    import * as reduxModule from 'redux';
    import {applyMiddleware, compose, createStore} from 'redux';
    import createReducer from './reducers';
    import signalRMiddleware from './middlewares/signalRMiddleware';
    import thunk from 'redux-thunk';

    /*
    Fix for Firefox redux dev tools extension
    https://github.com/zalmoxisus/redux-devtools-instrument/pull/19#issuecomment-400637274
     */
    reduxModule.__DO_NOT_USE__ActionTypes.REPLACE = '@@redux/INIT';

    const composeEnhancers =
        process.env.NODE_ENV !== 'production' &&
        typeof window === 'object' &&
        window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
            window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
                // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
            }) : compose;

    const enhancer = composeEnhancers(
        applyMiddleware(
            thunk,
            signalRMiddleware
            )
        // other store enhancers if any
    );

    const store = createStore(createReducer(), enhancer);

    store.asyncReducers = {};

    export const injectReducer = (key, reducer) => {
        if ( store.asyncReducers[key] )
        {
            return;
        }
        store.asyncReducers[key] = reducer;
        store.replaceReducer(createReducer(store.asyncReducers));
        return store;
    };

    export default store;

中间件文件


    import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';
    import * as Actions from "../../main/apps/chat/store/actions";

    const connection = new HubConnectionBuilder().withUrl("https://localhost:5001/chatHub")
                                            .configureLogging(LogLevel.Information)
                                            .withAutomaticReconnect()
                                            .build();

    connection.start();

    export default function signalRMiddleware() { 

            connection.on('ReceiveMessage', data => {
                Actions.receiveSocketMessage(data)
            })

            return  next => action => {

            switch (action.type) {

                case Actions.DIRECT_MESSAGE: 
                    connection.invoke('DirectMessage', action.payload);
            }

            return next(action);
        }
    }

聊天动作


    import axios from 'axios';
    import {setselectedContactId} from './contacts.actions';
    import {closeMobileChatsSidebar} from './sidebars.actions';

    export const GET_CHAT = '[CHAT APP] GET CHAT';
    export const REMOVE_CHAT = '[CHAT APP] REMOVE CHAT';
    export const SEND_MESSAGE = '[CHAT APP] SEND MESSAGE';
    export const DIRECT_MESSAGE = '[CHAT APP] DIRECT MESSAGE';
    export const RECEIVE_MESSAGE = '[CHAT APP] RECEIVE MESSAGE';

    export function receiveSocketMessage(data)
    {
        return (dispatch) => {
            dispatch({type: RECEIVE_MESSAGE, payload: `test`})
        }
    }

    export function directMessage(data)
    {
        return {
            type : DIRECT_MESSAGE,
            payload : data
        }
    }

聊天工具


    import * as Actions from '../actions';

    const initialState = null;

    const chat = function (state = initialState, action) {
        switch ( action.type )
        {
            case Actions.RECEIVE_MESSAGE:
            {
                return {
                    ...state,
                    directMessage: action.payload
                }
            }
            default:
            {
                return state;
            }
        }
    };

    export default chat;

聊天组件


    import React, {useEffect} from 'react';
    import {useTranslation} from 'react-i18next';
    import {Drawer, AppBar, Toolbar, Typography, IconButton, Hidden, Avatar, Icon, Paper, Button} from '@material-ui/core';
    import {fade} from '@material-ui/core/styles/colorManipulator';
    import {useDispatch, useSelector} from 'react-redux';
    import clsx from 'clsx';
    import withReducer from 'app/store/withReducer';
    import * as Actions from "./store/actions";
    import Chat from "./Chat";
    import ChatsSidebar from "./ChatsSidebar";
    import StatusIcon from "./StatusIcon";
    import ContactSidebar from './ContactSidebar';
    import UserSidebar from './UserSidebar';
    import reducer from './store/reducers';
    import {makeStyles} from '@material-ui/styles';

    const drawerWidth = 400;
    const headerHeight = 200;

    const useStyles = makeStyles(theme => ({
        root              : {
            display        : 'flex',
            flexDirection  : 'row',
            minHeight      : '100%',
            position       : 'relative',
            flex           : '1 1 auto',
            height         : 'auto',
            backgroundColor: theme.palette.background.default
        },
        topBg             : {
            position       : 'absolute',
            left           : 0,
            right          : 0,
            top            : 0,
            height         : headerHeight,
            backgroundImage: 'url("../../assets/images/backgrounds/header-bg.png")',
            backgroundColor: theme.palette.primary.dark,
            backgroundSize : 'cover',
            pointerEvents  : 'none'
        },
        contentCardWrapper: {
            position                      : 'relative',
            padding                       : 24,
            maxWidth                      : 1400,
            display                       : 'flex',
            flexDirection                 : 'column',
            flex                          : '1 0 auto',
            width                         : '100%',
            minWidth                      : '0',
            maxHeight                     : '95%',
            margin                        : '0 auto',
            [theme.breakpoints.down('sm')]: {
                padding: 16
            },
            [theme.breakpoints.down('xs')]: {
                padding: 12
            }
        },
        contentCard       : {
            display        : 'flex',
            position       : 'relative',
            flex           : '1 1 100%',
            flexDirection  : 'row',
            backgroundColor: "f7f7f7",
            boxShadow      : theme.shadows[1],
            borderRadius   : 8,
            minHeight      : 0,
            overflow       : 'hidden'
        },
        drawerPaper       : {
            width                       : drawerWidth,
            maxWidth                    : '100%',
            overflow                    : 'hidden',
            height                      : '100%',
            [theme.breakpoints.up('md')]: {
                position: 'relative'
            }
        },
        contentWrapper    : {
            display      : 'flex',
            flexDirection: 'column',
            flex         : '1 1 100%',
            zIndex       : 10,
            background   : `linear-gradient(to bottom, ${fade(theme.palette.background.paper, 0.8)} 0,${fade(theme.palette.background.paper, 0.6)} 20%,${fade(theme.palette.background.paper, 0.8)})`
        },
        content           : {
            display  : 'flex',
            flex     : '1 1 100%',
            minHeight: 0
        }
    }));

    function ChatApp(props)
    {

        const { t } = useTranslation();
        const dispatch = useDispatch();
        const chat = useSelector(({chatApp}) => chatApp.chat);
        const contacts = useSelector(({chatApp}) => chatApp.contacts.entities);
        const selectedContactId = useSelector(({chatApp}) => chatApp.contacts.selectedContactId);
        const mobileChatsSidebarOpen = useSelector(({chatApp}) => chatApp.sidebars.mobileChatsSidebarOpen);
        const userSidebarOpen = useSelector(({chatApp}) => chatApp.sidebars.userSidebarOpen);
        const contactSidebarOpen = useSelector(({chatApp}) => chatApp.sidebars.contactSidebarOpen);
        const directMessage = useSelector(({chatApp}) => chatApp.directMessage);

        const classes = useStyles(props);
        const selectedContact = contacts.find(_contact => (_contact.id === selectedContactId));

        useEffect(() => {
            dispatch(Actions.getUserData());
            dispatch(Actions.getContacts());
            dispatch(Actions.directMessage({message: "123 testando...", to: "Carlos"}));
        }, [dispatch]);

        useEffect(() => {
            console.log(`Mensagem recebido: ${directMessage} TAMO NO APP`);
        }, [directMessage])

        return (
            <div className={clsx(classes.root)}>

                <div className={clsx(classes.contentCardWrapper, 'container')}>

                    <div className={classes.contentCard}>

                        <Hidden mdUp>
                            <Drawer
                                className="h-full absolute z-20"
                                variant="temporary"
                                anchor="left"
                                open={mobileChatsSidebarOpen}
                                onClose={() => dispatch(Actions.closeMobileChatsSidebar())}
                                classes={{
                                    paper: clsx(classes.drawerPaper, "absolute left-0")
                                }}
                                style={{position: 'absolute'}}
                                ModalProps={{
                                    keepMounted  : true,
                                    disablePortal: true,
                                    BackdropProps: {
                                        classes: {
                                            root: "absolute"
                                        }
                                    }
                                }}
                            >
                                <ChatsSidebar/>
                            </Drawer>
                        </Hidden>
                        <Hidden smDown>
                            <Drawer
                                className="h-full z-20"
                                variant="permanent"
                                open
                                classes={{
                                    paper: classes.drawerPaper
                                }}
                            >
                                <ChatsSidebar/>
                            </Drawer>
                        </Hidden>
                        <Drawer
                            className="h-full absolute z-30"
                            variant="temporary"
                            anchor="left"
                            open={userSidebarOpen}
                            onClose={() => dispatch(Actions.closeUserSidebar())}
                            classes={{
                                paper: clsx(classes.drawerPaper, "absolute left-0")
                            }}
                            style={{position: 'absolute'}}
                            ModalProps={{
                                keepMounted  : false,
                                disablePortal: true,
                                BackdropProps: {
                                    classes: {
                                        root: "absolute"
                                    }
                                }
                            }}
                        >
                            <UserSidebar/>
                        </Drawer>

                        <main className={clsx(classes.contentWrapper, "z-10")}>
                            {!chat ?
                                (
                                    <>
                                        <AppBar position="static" elevation={1}>
                                            <Toolbar className="px-16"/>
                                        </AppBar>
                                        <div className="flex flex-col flex-1 items-center justify-center p-24">
                                            <Paper className="rounded-full p-48">
                                                <Icon className="block text-64" color="secondary">chat</Icon>
                                            </Paper>
                                            <Typography variant="h6" className="my-24">{t("Chat")}</Typography>
                                            <Typography className="hidden md:flex px-16 pb-24 mt-24 text-center" color="textSecondary">
                                                {t("Select a contact to start a conversation!")}
                                            </Typography>
                                            <Button variant="outlined" color="primary" className="flex md:hidden normal-case" onClick={() => dispatch(Actions.openMobileChatsSidebar())}>
                                                {t("Select a contact to start a conversation!")}
                                            </Button>                                        
                                        </div>
                                    </>
                                ) : (
                                    <>
                                        <AppBar position="static" elevation={1}>
                                            <Toolbar className="px-16">
                                                <IconButton
                                                    color="inherit"
                                                    aria-label="Open drawer"
                                                    onClick={() => dispatch(Actions.openMobileChatsSidebar())}
                                                    className="flex md:hidden"
                                                >
                                                    <Icon>chat</Icon>
                                                </IconButton>
                                                <div className="flex items-center cursor-pointer" onClick={() => dispatch(Actions.openContactSidebar())}>
                                                    <div className="relative ml-8 mr-12">
                                                        <div className="absolute right-0 bottom-0 -m-4 z-10">
                                                            <StatusIcon status={selectedContact.status}/>
                                                        </div>

                                                        <Avatar src={selectedContact.avatar} alt={selectedContact.name}>
                                                            {!selectedContact.avatar || selectedContact.avatar === '' ? selectedContact.name[0] : ''}
                                                        </Avatar>
                                                    </div>
                                                    <Typography color="inherit" className="text-18 font-600">{selectedContact.name}</Typography>
                                                </div>
                                            </Toolbar>
                                        </AppBar>

                                        <div className={classes.content}>
                                            <Chat className="flex flex-1 z-10"/>
                                        </div>
                                    </>
                                )
                            }
                        </main>

                        <Drawer
                            className="h-full absolute z-30"
                            variant="temporary"
                            anchor="right"
                            open={contactSidebarOpen}
                            onClose={() => dispatch(Actions.closeContactSidebar())}
                            classes={{
                                paper: clsx(classes.drawerPaper, "absolute right-0")
                            }}
                            style={{position: 'absolute'}}
                            ModalProps={{
                                keepMounted  : true,
                                disablePortal: true,
                                BackdropProps: {
                                    classes: {
                                        root: "absolute"
                                    }
                                }
                            }}
                        >
                            <ContactSidebar/>
                        </Drawer>
                    </div>
                </div>
            </div>
        );
    }

    export default withReducer('chatApp', reducer)(ChatApp);
javascript redux react-redux asp.net-core-signalr ecmascript-next
1个回答
0
投票
- export default function signalRMiddleware() { + export default function signalRMiddleware(api) { connection.on('ReceiveMessage', data => { - Actions.receiveSocketMessage(data) + api.dispatch(Actions.receiveSocketMessage(data)) }) return next => action => { switch (action.type) { case Actions.DIRECT_MESSAGE: connection.invoke('DirectMessage', action.payload); } return next(action); } }
© www.soinside.com 2019 - 2024. All rights reserved.