programing

리액트 리덕스 및 웹 소켓(소켓 포함)이오

showcode 2023. 3. 11. 09:41
반응형

리액트 리덕스 및 웹 소켓(소켓 포함)이오

저는 이 React-Redux 기술을 처음 사용하는 사람입니다. 구현에 도움을 받고 싶습니다.

소켓이 있는 채팅어플리케이션(socket.io)을 1개 실장하고 싶습니다.먼저 사용자가 등록해야 합니다(서버 측에서 여권을 사용). 등록이 성공하면 사용자는 webSocket에 접속해야 합니다.

파이프와 같은 미들웨어를 사용하여 모든 액션을 수행하고 미들웨어가 어떤 액션을 취하느냐에 따라 다른 액션을 하는 것이 가장 좋다고 생각했습니다.

작업 유형이 다음과 같은 경우AUTH_USER클라이언트와 서버의 접속을 확립하고, 서버로부터의 모든 이벤트를 설정합니다.이벤트를 설정합니다.

작업 유형이 다음과 같은 경우MESSAGE메시지를 서버로 보냅니다.

코드 조각:

-----socketMiddleware.js -----

import { AUTH_USER,  MESSAGE } from '../actions/types';

import * as actions from 'actions/socket-actions';

import io from 'socket.io-client';

const socket = null;

export default function ({ dispatch }) {

    return next => action => {

        if(action.type == AUTH_USER) {

            socket = io.connect(`${location.host}`);

            socket.on('message', data => {

               store.dispatch(actions.addResponse(action.data));

            });

        }

        else if(action.type == MESSAGE && socket) {

            socket.emit('user-message', action.data);

            return next(action)

        } else {
            return next(action)
        }
    }

}

-----index.disc -----

import {createStore, applyMiddleware} from 'redux';

import socketMiddleware from './socketMiddleware';



const createStoreWithMiddleware = applyMiddleware(

  socketMiddleware

)(createStore);

const store = createStoreWithMiddleware(reducer);

<Provider store={store}>

    <App />

</Provider>

그 연습에 대해 어떻게 생각하세요, 더 나은 구현인가요?

스포일러:저는 현재 오픈소스 채팅 어플리케이션을 개발하고 있습니다.

동작을 미들웨어에서 분리하여 소켓클라이언트도 미들웨어에서 분리함으로써 작업을 개선할 수 있습니다.따라서 다음과 같은 결과가 발생합니다.

  • 모든 요청 유형 -> REQUEST, SUCCESS, FAILURE 유형(필수 아님)
  • 리듀서 -> 다른 상태를 저장합니다.
  • 액션 -> 접속/절단/발신/리슨 액션을 송신합니다.
  • Middleware -> - 동작을 처리하여 현재 액션을 소켓클라이언트에 전달 또는 전달
  • 클라이언트 -> 소켓클라이언트(socket.io).

아래 코드는 개발 중인 실제 앱(경우에 따라서는 약간 편집)에서 가져온 것으로, 대부분의 경우 충분하지만 Socket Client와 같은 일부 기능은 100% 완벽하지 않을 수 있습니다.

행동들

당신은 가능한 한 간단한 행동을 원한다. 왜냐하면 그것들은 종종 반복적인 일이기 때문에 당신은 결국 많은 일을 하게 될 것이기 때문이다.

export function send(chatId, content) {
  const message = { chatId, content };
  return {
    type: 'socket',
    types: [SEND, SEND_SUCCESS, SEND_FAIL],
    promise: (socket) => socket.emit('SendMessage', message),
  }
}

소켓은 파라미터화된 함수이기 때문에 어플리케이션 전체에서 동일한 소켓인스턴스를 공유할 수 있기 때문에 Import에 대해 걱정할 필요가 없습니다(나중에 설명하겠습니다).

미들웨어(SocketMiddleware.js):

AJAX 대신 소켓에 errikras/react-redux-universal-hot-example과 유사한 전략을 사용합니다.

당사의 소켓 미들웨어는 소켓 요청만 처리합니다.

미들웨어는 이 액션을 소켓클라이언트에 전달하고 다음 처리를 디스패치합니다.

  • )types[0] 중 요청 중( ): 요청 중( )action.type리듀서에 송신됩니다).
  • SUCCESS (액션 성공))types[1] 시(): " " " " (action.type은 「」으로 합니다.action.result리듀서에 송신됩니다).
  • )types[2] 실패 시(): " " " " (action.type은 「」으로 합니다.action.error리듀서에 송신됩니다).
export default function socketMiddleware(socket) {
  // Socket param is the client. We'll show how to set this up later.
  return ({dispatch, getState}) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState);
    }

    /*
     * Socket middleware usage.
     * promise: (socket) => socket.emit('MESSAGE', 'hello world!')
     * type: always 'socket'
     * types: [REQUEST, SUCCESS, FAILURE]
     */
    const { promise, type, types, ...rest } = action;

    if (type !== 'socket' || !promise) {
      // Move on! Not a socket request or a badly formed one.
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({...rest, type: REQUEST});

    return promise(socket)
      .then((result) => {
        return next({...rest, result, type: SUCCESS });
      })
      .catch((error) => {
        return next({...rest, error, type: FAILURE });
      })
  };
}

SocketClient.js

socket.io-client를 로드 및 관리하는 유일한 사용자입니다.

[선택사항] (아래 코드 1 참조)socket.io에 관한 매우 흥미로운 기능 중 하나는 메시지 확인 응답을 할 수 있다는 것입니다.이것은 HTTP 요구를 실행할 때의 일반적인 응답입니다.각 요청이 올바른지 확인하기 위해 사용할 수 있습니다.이 기능 서버를 사용하려면 socket.io 명령어에도 최신 확인 응답 파라미터가 필요합니다.

import io from 'socket.io-client';

// Example conf. You can move this to your config file.
const host = 'http://localhost:3000';
const socketPath = '/api/socket.io';

export default class socketAPI {
  socket;

  connect() {
    this.socket = io.connect(host, { path: socketPath });
    return new Promise((resolve, reject) => {
      this.socket.on('connect', () => resolve());
      this.socket.on('connect_error', (error) => reject(error));
    });
  }

  disconnect() {
    return new Promise((resolve) => {
      this.socket.disconnect(() => {
        this.socket = null;
        resolve();
      });
    });
  }

  emit(event, data) {
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');

      return this.socket.emit(event, data, (response) => {
        // Response is the optional callback that you can use with socket.io in every request. See 1 above.
        if (response.error) {
          console.error(response.error);
          return reject(response.error);
        }

        return resolve();
      });
    });
  }

  on(event, fun) {
    // No promise is needed here, but we're expecting one in the middleware.
    return new Promise((resolve, reject) => {
      if (!this.socket) return reject('No socket connection.');

      this.socket.on(event, fun);
      resolve();
    });
  }
}

app.module

★★★★★를 초기화합니다.SocketClient이치노

const socketClient = new SocketClient();
const store = configureStore(initialState, socketClient, apiClient);

configureStore.js

해서 ㅇㅇㅇㅇㅇㅇㅇㅇㅇ를 .socketMiddleware 된 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.SocketClient(이것들)

export default function configureStore(initialState, socketClient, apiClient) {
const loggerMiddleware = createLogger();
const middleware = [
  ...
  socketMiddleware(socketClient),
  ...
];

[별거 없음] 동작 유형 상수

특별한 일 없음 = 평소에는 주로 하던 일.

const SEND = 'redux/message/SEND';
const SEND_SUCCESS = 'redux/message/SEND_SUCCESS';
const SEND_FAIL = 'redux/message/SEND_FAIL';

[특별한 거 없어]리듀서

export default function reducer(state = {}, action = {}) {
  switch(action.type) {
    case SEND: {
      return {
        ...state,
        isSending: true,
      };
    }
    default: {
      return state;
    }
  }
}

수고가 많아 보일지 모르지만, 일단 셋업을 하면 그만한 가치가 있습니다.관련 코드는 읽기 쉽고 디버깅이 용이하며 실수하기 쉽습니다.

PS: AJAX API 호출에서도 이 전략을 따를 수 있습니다.

이를 위해 @reduxjs/toolkit의 기능을 사용했습니다.다음과 같은 유형을 자동으로 생성합니다.pending,fulfilled ★★★★★★★★★★★★★★★★★」rejected.

저는 그의 답변에서 @zurfyx와 같은 socketService를 사용했습니다.

동작은 다음과 같습니다.

const sendMessage = createAsyncThunk(
  'game/send-message',
  async function (text, { getState }) {
    const roomToken = selectRoomToken(getState());
    return await socketService.emit('send-message', { text, roomToken });
  }
);

리듀서는 다음과 같습니다.

const gameSlice = createSlice({
  name: 'game',
  initialState: { },
  reducers: {},
  extraReducers: {
    [sendMessage.pending]: (state, action) => {
      state.messages.push({
        id: action.meta.requestId,
        text: action.meta.arg,
        my: true,
      });
    },
    [sendMessage.rejected]: (state, action) => {
      state.messages = state.messages.filter(
        ms => ms.id !== action.meta.requestId
      );
    },
  },
});

미들웨어와 함께 작동하는 솔루션:

import { Middleware } from 'redux';
import { io, Socket } from 'socket.io-client';

import { RootState } from '../index';
import { SERVER_URL } from '../../api/consts';
import { actions } from '../features/chat/slice';

export const socketMiddleware: Middleware = (store) => {
    let socket: Socket;

    return (next) => (action) => {
        const state = store.getState() as RootState;
        const needInitSocket = state.auth.isAuth && !socket;

        if (needInitSocket) {
            socket = io(SERVER_URL, { transports: ['websocket', 'polling'], withCredentials: true });

            socket.on('connect', () => {
                store.dispatch(actions.connectionEstablished());
            });

            socket.on('YOUR_EVENT', (data) => {
               store.dispatch(actions.doSomething(data));
            })
        }

        // to emit data to server
        if (actions.sendMessage.match(action) && socket) {
            socket.emit('ON_ROOM_MESSAGE', action.payload);
        }

        next(action);
    };
};

레독스 툴킷으로 작업했습니다.

언급URL : https://stackoverflow.com/questions/37876889/react-redux-and-websockets-with-socket-io

반응형