佴 芬嘉

佴 芬嘉

1678471500

什么是 Redux 中间件以及如何将其与 WebSockets 一起使用

在本文中,您将了解什么是 Redux 中间件以及如何将它与 WebSockets 一起使用。如果您使用的是 Redux,中间件是连接到 WebSockets 的好地方。 

我们还讨论了很多不同的服务器技术,比如 Express。一个常见的概念是中间件。在 redux 中,它具有相同的目的。如果您想了解有关 Express 的更多信息,请查看 TypeScript Express 教程 #1。中间件、路由和控制器

在这里,我们使用带有 TypeScript 的 Redux 工具包。要获得相同的初始模型,请运行

npx create-react-app my-app --template redux-typescript.

Redux 中间件思想

中间件允许您在用户提交操作和它到达 reducer 之间添加逻辑。中间件最简单的用例之一是日志记录。

可能有点令人困惑的是,在 Redux 中间件中使用currying。这是一种将带有多个参数的函数分解为一系列函数的方法,每个函数只带有一个参数。Dan Abramov 对此的解释是我们可能希望使用 JavaScript 闭包来发挥我们的优势。虽然不是每个人都同意这个设计决定,但在此阶段进行重大更改以修复它可能不值得。

为了理解这一点,让我们创建一个简单的例子。

记录器中间件.tsx

import { Middleware } from 'redux'

const loggerMiddleware: Middleware = (store) => {
  return (next) => {
    return (action) => {
      console.log('dispatching', action);
      const result = next(action);
      console.log('next state', store.getState());
      return result;
    }
  }
}

export default loggerMiddleware;

 

我们可以用更短的形式编写上面的代码,但现在让我们非常明确。

当我们将上述中间件添加到我们的商店时,它会导致我们提交的所有操作都被记录下来。

流中间件

nextargument 是上面代码中最需要理解的东西。因为我们可以有多个中间件,下一个函数调用链中的下一个。如果没有剩余的中间件,它会发送一个动作。

 

如果我们不打电话

nextfunction 动作将不会被分派。

 

让我们从官方文档中添加另一个示例以更好地表示它。

崩溃中间件.tsx

import { Middleware } from 'redux'

const crashMiddleware: Middleware = store => next => action => {
  console.log('crashMiddleware');
  try {
    return next(action)
  } catch (error) {
    console.error('Caught an exception!', error)
    throw error;
  }
}

export default crashMiddleware;

在本文中,我们使用Redux Toolkit。它立即将中间件添加到我们的商店。要在保持默认值的同时添加新的中间件,我们可以使用

getDefaultMiddleware 如文档所示。

商店.tsx

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import loggerMiddleware from './loggerMiddleware';
import crashMiddleware from './crashMiddleware';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
  middleware: (getDefaultMiddleware) => {
    return getDefaultMiddleware().concat([crashMiddleware, loggerMiddleware])
  },
});

 

关于

counterReducer 与本文开头提到的 redux-typescript 模板捆绑在一起。

 

感谢我们添加的内容

CrashMiddleware 在 loggerMiddleware 之前,它先运行。

 

如果我们不打电话

crashMiddleware 中的 nextfunction,loggerMiddleware 永远不会运行,也永远不会分派 action。

 

Usando WebSockets 中间件

在带有 NestJS #26 的 API中。使用 WebSockets 进行实时聊天,我们已经使用 socket.io 为聊天应用程序创建了一个后端。我们看到有些事件可能会发生。让我们创建一个包含它们的枚举:

虽然我们在本文中使用的是 socket.io,但它与简单的网络套接字非常相似。

聊天事件.tsx

enum ChatEvent {
  SendMessage = 'send_message',
  RequestAllMessages = 'request_all_messages',
  SendAllMessages = 'send_all_messages',
  ReceiveMessage = 'receive_message'
}

export default ChatEvent;

 

连接建立

要建立连接,我们需要服务器应用程序的 URL。将它保留在前端的一个好方法是使用环境变量。

.env

REACT_APP_API_URL=http://localhost:3000

反应-应用程序-env.d.ts

/// 

namespace NodeJS {
  interface ProcessEnv {
    REACT_APP_API_URL: string;
  }
}

我们还使用基本操作定义切片器的基础知识。

聊天切片.tsx

import { createSlice } from '@reduxjs/toolkit';

export interface ChatState {
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

建立连接的最后一部分是创建中间件。我们在本文中创建的 API需要 cookie 身份验证。为了解决这个问题,我们需要

withCredential参数。

聊天中间件.tsx

import { Middleware } from 'redux'
import { io } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';

const chatMiddleware: Middleware = store => next => action => {
  if (!chatActions.startConnecting.match(action)) {
    return next(action);
  }

  const socket = io(process.env.REACT_APP_API_URL, {
    withCredentials: true,
  });

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

  next(action);
}

export default chatMiddleware;

称呼

chatActions.startConnecting.match 函数是我们检查动作是否与切片中定义的动作匹配的方法。

 

当用户发送时,我们的应用程序通过 WebSockets 建立连接

开始连接动作。

发送和接收聊天消息

来自我们后端的所有消息都具有特定的数据结构。那么,我们来创建一个与之对应的接口。

聊天消息.tsx

interface ChatMessage {
  id: number;
  content: string;
  author: {
    email: string;
  }
}

export default ChatMessage;

现在我们有了上面的接口,我们可以在我们的片段中使用它。

聊天切片.tsx

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import ChatMessage from "./chatMessage";

export interface ChatState {
  messages: ChatMessage[];
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  messages: [],
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
    receiveAllMessages: ((state, action: PayloadAction<{
      messages: ChatMessage[]
    }>) => {
      state.messages = action.payload.messages;
    }),
    receiveMessage: ((state, action: PayloadAction<{
      message: ChatMessage
    }>) => {
      state.messages.push(action.payload.message);
    }),
    submitMessage: ((state, action: PayloadAction<{
      content: string
    }>) => {
      return;
    })
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

发送和接收消息的最后一部分是监听所有事件并发送它们。

聊天中间件.tsx

import { Middleware } from 'redux'
import { io, Socket } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';
import ChatMessage from "./chatMessage";

const chatMiddleware: Middleware = store => {
  let socket: Socket;

  return next => action => {
    const isConnectionEstablished = socket && store.getState().chat.isConnected;

    if (chatActions.startConnecting.match(action)) {
      socket = io(process.env.REACT_APP_API_URL, {
        withCredentials: true,
      });

      socket.on('connect', () => {
        store.dispatch(chatActions.connectionEstablished());
        socket.emit(ChatEvent.RequestAllMessages);
      })

      socket.on(ChatEvent.SendAllMessages, (messages: ChatMessage[]) => {
        store.dispatch(chatActions.receiveAllMessages({ messages }));
      })

      socket.on(ChatEvent.ReceiveMessage, (message: ChatMessage) => {
        store.dispatch(chatActions.receiveMessage({ message }));
      })
    }

    if (chatActions.submitMessage.match(action) && isConnectionEstablished) {
      socket.emit(ChatEvent.SendMessage, action.payload.content);
    }

    next(action);
  }
}

export default chatMiddleware;

在上面的代码中,我们执行以下操作:

  • 应用程序建立连接后立即引发 ChatEvent.RequestAllMessage 事件。
  • 侦听后端为响应 ChatEvent.RequestAllMessage 事件而触发的 ChatEvent.SendAllMessage 事件。
  • 当用户提交 chatActions.submitMessage 操作时引发 ChatEvent.SendMessage 事件。
  • 侦听任何用户发送消息时后端触发的 ChatEvent.ReceiveMessage 事件。

概括

在本文中,我们将解释什么是 Redux 上下文中的中间件。为此,我们使用了 Redux 工具包和 TypeScript。因为中间件存在于我们应用程序的整个生命周期中,所以这是连接到 WebSocket 的好地方。中间件还可以访问当前状态并可以调度操作。它可以响应传入的消息并在用户提交操作时发出它们。

原文检索自:https: //wanago.io

#websocket  #redux 

What is GEEK

Buddha Community

什么是 Redux 中间件以及如何将其与 WebSockets 一起使用
佴 芬嘉

佴 芬嘉

1678471500

什么是 Redux 中间件以及如何将其与 WebSockets 一起使用

在本文中,您将了解什么是 Redux 中间件以及如何将它与 WebSockets 一起使用。如果您使用的是 Redux,中间件是连接到 WebSockets 的好地方。 

我们还讨论了很多不同的服务器技术,比如 Express。一个常见的概念是中间件。在 redux 中,它具有相同的目的。如果您想了解有关 Express 的更多信息,请查看 TypeScript Express 教程 #1。中间件、路由和控制器

在这里,我们使用带有 TypeScript 的 Redux 工具包。要获得相同的初始模型,请运行

npx create-react-app my-app --template redux-typescript.

Redux 中间件思想

中间件允许您在用户提交操作和它到达 reducer 之间添加逻辑。中间件最简单的用例之一是日志记录。

可能有点令人困惑的是,在 Redux 中间件中使用currying。这是一种将带有多个参数的函数分解为一系列函数的方法,每个函数只带有一个参数。Dan Abramov 对此的解释是我们可能希望使用 JavaScript 闭包来发挥我们的优势。虽然不是每个人都同意这个设计决定,但在此阶段进行重大更改以修复它可能不值得。

为了理解这一点,让我们创建一个简单的例子。

记录器中间件.tsx

import { Middleware } from 'redux'

const loggerMiddleware: Middleware = (store) => {
  return (next) => {
    return (action) => {
      console.log('dispatching', action);
      const result = next(action);
      console.log('next state', store.getState());
      return result;
    }
  }
}

export default loggerMiddleware;

 

我们可以用更短的形式编写上面的代码,但现在让我们非常明确。

当我们将上述中间件添加到我们的商店时,它会导致我们提交的所有操作都被记录下来。

流中间件

nextargument 是上面代码中最需要理解的东西。因为我们可以有多个中间件,下一个函数调用链中的下一个。如果没有剩余的中间件,它会发送一个动作。

 

如果我们不打电话

nextfunction 动作将不会被分派。

 

让我们从官方文档中添加另一个示例以更好地表示它。

崩溃中间件.tsx

import { Middleware } from 'redux'

const crashMiddleware: Middleware = store => next => action => {
  console.log('crashMiddleware');
  try {
    return next(action)
  } catch (error) {
    console.error('Caught an exception!', error)
    throw error;
  }
}

export default crashMiddleware;

在本文中,我们使用Redux Toolkit。它立即将中间件添加到我们的商店。要在保持默认值的同时添加新的中间件,我们可以使用

getDefaultMiddleware 如文档所示。

商店.tsx

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import loggerMiddleware from './loggerMiddleware';
import crashMiddleware from './crashMiddleware';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
  middleware: (getDefaultMiddleware) => {
    return getDefaultMiddleware().concat([crashMiddleware, loggerMiddleware])
  },
});

 

关于

counterReducer 与本文开头提到的 redux-typescript 模板捆绑在一起。

 

感谢我们添加的内容

CrashMiddleware 在 loggerMiddleware 之前,它先运行。

 

如果我们不打电话

crashMiddleware 中的 nextfunction,loggerMiddleware 永远不会运行,也永远不会分派 action。

 

Usando WebSockets 中间件

在带有 NestJS #26 的 API中。使用 WebSockets 进行实时聊天,我们已经使用 socket.io 为聊天应用程序创建了一个后端。我们看到有些事件可能会发生。让我们创建一个包含它们的枚举:

虽然我们在本文中使用的是 socket.io,但它与简单的网络套接字非常相似。

聊天事件.tsx

enum ChatEvent {
  SendMessage = 'send_message',
  RequestAllMessages = 'request_all_messages',
  SendAllMessages = 'send_all_messages',
  ReceiveMessage = 'receive_message'
}

export default ChatEvent;

 

连接建立

要建立连接,我们需要服务器应用程序的 URL。将它保留在前端的一个好方法是使用环境变量。

.env

REACT_APP_API_URL=http://localhost:3000

反应-应用程序-env.d.ts

/// 

namespace NodeJS {
  interface ProcessEnv {
    REACT_APP_API_URL: string;
  }
}

我们还使用基本操作定义切片器的基础知识。

聊天切片.tsx

import { createSlice } from '@reduxjs/toolkit';

export interface ChatState {
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

建立连接的最后一部分是创建中间件。我们在本文中创建的 API需要 cookie 身份验证。为了解决这个问题,我们需要

withCredential参数。

聊天中间件.tsx

import { Middleware } from 'redux'
import { io } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';

const chatMiddleware: Middleware = store => next => action => {
  if (!chatActions.startConnecting.match(action)) {
    return next(action);
  }

  const socket = io(process.env.REACT_APP_API_URL, {
    withCredentials: true,
  });

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

  next(action);
}

export default chatMiddleware;

称呼

chatActions.startConnecting.match 函数是我们检查动作是否与切片中定义的动作匹配的方法。

 

当用户发送时,我们的应用程序通过 WebSockets 建立连接

开始连接动作。

发送和接收聊天消息

来自我们后端的所有消息都具有特定的数据结构。那么,我们来创建一个与之对应的接口。

聊天消息.tsx

interface ChatMessage {
  id: number;
  content: string;
  author: {
    email: string;
  }
}

export default ChatMessage;

现在我们有了上面的接口,我们可以在我们的片段中使用它。

聊天切片.tsx

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import ChatMessage from "./chatMessage";

export interface ChatState {
  messages: ChatMessage[];
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  messages: [],
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
    receiveAllMessages: ((state, action: PayloadAction<{
      messages: ChatMessage[]
    }>) => {
      state.messages = action.payload.messages;
    }),
    receiveMessage: ((state, action: PayloadAction<{
      message: ChatMessage
    }>) => {
      state.messages.push(action.payload.message);
    }),
    submitMessage: ((state, action: PayloadAction<{
      content: string
    }>) => {
      return;
    })
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

发送和接收消息的最后一部分是监听所有事件并发送它们。

聊天中间件.tsx

import { Middleware } from 'redux'
import { io, Socket } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';
import ChatMessage from "./chatMessage";

const chatMiddleware: Middleware = store => {
  let socket: Socket;

  return next => action => {
    const isConnectionEstablished = socket && store.getState().chat.isConnected;

    if (chatActions.startConnecting.match(action)) {
      socket = io(process.env.REACT_APP_API_URL, {
        withCredentials: true,
      });

      socket.on('connect', () => {
        store.dispatch(chatActions.connectionEstablished());
        socket.emit(ChatEvent.RequestAllMessages);
      })

      socket.on(ChatEvent.SendAllMessages, (messages: ChatMessage[]) => {
        store.dispatch(chatActions.receiveAllMessages({ messages }));
      })

      socket.on(ChatEvent.ReceiveMessage, (message: ChatMessage) => {
        store.dispatch(chatActions.receiveMessage({ message }));
      })
    }

    if (chatActions.submitMessage.match(action) && isConnectionEstablished) {
      socket.emit(ChatEvent.SendMessage, action.payload.content);
    }

    next(action);
  }
}

export default chatMiddleware;

在上面的代码中,我们执行以下操作:

  • 应用程序建立连接后立即引发 ChatEvent.RequestAllMessage 事件。
  • 侦听后端为响应 ChatEvent.RequestAllMessage 事件而触发的 ChatEvent.SendAllMessage 事件。
  • 当用户提交 chatActions.submitMessage 操作时引发 ChatEvent.SendMessage 事件。
  • 侦听任何用户发送消息时后端触发的 ChatEvent.ReceiveMessage 事件。

概括

在本文中,我们将解释什么是 Redux 上下文中的中间件。为此,我们使用了 Redux 工具包和 TypeScript。因为中间件存在于我们应用程序的整个生命周期中,所以这是连接到 WebSocket 的好地方。中间件还可以访问当前状态并可以调度操作。它可以响应传入的消息并在用户提交操作时发出它们。

原文检索自:https: //wanago.io

#websocket  #redux 

Reduce Redux Boilerplate Code with Redux-Actions

Redux has become one of the most popular libraries in front-end development since it was introduced by Dan Abramov and Andrew Clark in 2015. They designed it as the successor for Flux, with the support of some developer tools and a few more concepts embedded in it.

Flux is a fancy name for observer pattern further modified to support React. Both Flux and Redux consist of similar concepts like Store, Actions (events in the application). In other words, Flux is a simple JavaScript object but with some middleware like redux-thunk. It can be a function or a promise for Redux. However, Redux is a single source of truth with concepts like immutability, which improve performance. It is one of the main reasons for Redux to dominate in State Management.

Image for post

Flux vs Redux comparison source: enappd.com

Despite its advantages, some developers have found it rather challenging to deal with Redux due to the amount of boilerplate code introduced with it. And the complexity of the code seems to be another reason for the difficulty.

In this article, we will look at how to reduce the boilerplate code brought about by Actions and Reducers using Redux-Actions

#react-redux-boilerplate #react-redux #react #react-actions #redux

Aisu  Joesph

Aisu Joesph

1678143000

What Is Redux Middleware and How to Use It with WebSockets

In this article, learn about What Is Redux Middleware and How to Use It with WebSockets. Middleware is a great place to connect to WebSockets if you use Redux. 

In this blog, we also talk a lot about various backend technologies such as Express. One of the concepts popular there is middleware. In redux, it serves a similar purpose. If you want to know more about Express, check out TypeScript Express tutorial #1. Middleware, routing, and controllers

Here, we use Redux Toolkit with TypeScript. To get the same starting template, run

npx create-react-app my-app --template redux-typescript.

The idea behind Redux middleware

Middleware allows us to add logic between the moment the user dispatches the action and when it reaches the reducer. One of the most straightforward use-cases for the middleware is logging.

Something that might be a bit confusing is that in Redux, middleware uses currying. It is a technique that involves breaking down a function that takes multiple arguments into a chain of functions that take only one argument each. Dan Abramov justifies it by the fact that we might want to use the JavaScript closures to our advantage. While not everyone agrees with this design decision, it might not be worth creating a breaking change to fix it at this point.

To understand it, let’s create a simple example.

loggerMiddlewarre.tsx

import { Middleware } from 'redux'

const loggerMiddleware: Middleware = (store) => {
  return (next) => {
    return (action) => {
      console.log('dispatching', action);
      const result = next(action);
      console.log('next state', store.getState());
      return result;
    }
  }
}

export default loggerMiddleware;

 

We could write the above code in a shorter way, but let’s be very explicit for now.

When we add the above middleware to our store, it results in logging every action we dispatch.

Chaining middleware

nextargument is the most crucial thing to understand in the above code. Since we can have multiple middlewares, thenextfunction invokes the next one in the chain. If there are no middlewares left, it dispatches the action.

 

If we wouldn’t call the

nextfunction, the action wouldn’t be dispatched.

 

Let’s add another example from the official documentation to visualize it better.

crashMiddleware.tsx

import { Middleware } from 'redux'

const crashMiddleware: Middleware = store => next => action => {
  console.log('crashMiddleware');
  try {
    return next(action)
  } catch (error) {
    console.error('Caught an exception!', error)
    throw error;
  }
}

export default crashMiddleware;

In this article, we use the Redux Toolkit. It adds some middleware to our store out of the box. To add new middleware while retaining the default ones, we can use the

getDefaultMiddlewarefunction as the documentation suggests.

store.tsx

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import loggerMiddleware from './loggerMiddleware';
import crashMiddleware from './crashMiddleware';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
  middleware: (getDefaultMiddleware) => {
    return getDefaultMiddleware().concat([crashMiddleware, loggerMiddleware])
  },
});

 

The

counterReducercomes bundleded with theredux-typescripttemplate mentioned at the top of this article.

 

Thanks to the fact that we’ve added

crashMiddlewarebefore theloggerMiddleware, it executes first.

 

If we wouldn’t call the

nextfunction in thecrashMiddleware, theloggerMiddlewarewould never run and the action wouldn’t be dispatched.

 

Using WebSockets with middleware

In API with NestJS #26. Real-time chat with WebSockets, we’ve created a backend for a chat application using socket.io. We can see that there are a few events that can happen. Let’s create an enum that contains all of them:

Even though in this article we use socket.io, the could would be very similar with barebones WebSockets.

chatEvent.tsx

enum ChatEvent {
  SendMessage = 'send_message',
  RequestAllMessages = 'request_all_messages',
  SendAllMessages = 'send_all_messages',
  ReceiveMessage = 'receive_message'
}

export default ChatEvent;

 

Establishing a connection

To establish a connection, we need to have the URL of our backend application. A good way to store it on the frontend is through the environment variables.

.env

REACT_APP_API_URL=http://localhost:3000

react-app-env.d.ts

/// 

namespace NodeJS {
  interface ProcessEnv {
    REACT_APP_API_URL: string;
  }
}

Let’s also define the basics of our slice with the basic actions.

chatSlice.tsx

import { createSlice } from '@reduxjs/toolkit';

export interface ChatState {
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

The last part of establishing a connection is to create middleware. The API we’ve created in this article needs authentication through cookies. To handle it, we need the

withCredentialsargument.

chatMiddleware.tsx

import { Middleware } from 'redux'
import { io } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';

const chatMiddleware: Middleware = store => next => action => {
  if (!chatActions.startConnecting.match(action)) {
    return next(action);
  }

  const socket = io(process.env.REACT_APP_API_URL, {
    withCredentials: true,
  });

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

  next(action);
}

export default chatMiddleware;

Calling the

chatActions.startConnecting.matchfunction is how we can check if the action matches the one defined in the slice.

 

Our application establishes a connection through WebSockets when the user dispatches the

startConnectingaction.

Sending chat messages and receiving them

All of the messages from our backend have a particular data structure. So let’s create an interface that matches it.

chatMessage.tsx

interface ChatMessage {
  id: number;
  content: string;
  author: {
    email: string;
  }
}

export default ChatMessage;

Now that we have the above interface, we can use it in our slice.

chatSlice.tsx

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import ChatMessage from "./chatMessage";

export interface ChatState {
  messages: ChatMessage[];
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  messages: [],
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
    receiveAllMessages: ((state, action: PayloadAction<{
      messages: ChatMessage[]
    }>) => {
      state.messages = action.payload.messages;
    }),
    receiveMessage: ((state, action: PayloadAction<{
      message: ChatMessage
    }>) => {
      state.messages.push(action.payload.message);
    }),
    submitMessage: ((state, action: PayloadAction<{
      content: string
    }>) => {
      return;
    })
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

The last part of sending and receiving messages is to listen to all of the events and dispatch them.

chatMiddleware.tsx

import { Middleware } from 'redux'
import { io, Socket } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';
import ChatMessage from "./chatMessage";

const chatMiddleware: Middleware = store => {
  let socket: Socket;

  return next => action => {
    const isConnectionEstablished = socket && store.getState().chat.isConnected;

    if (chatActions.startConnecting.match(action)) {
      socket = io(process.env.REACT_APP_API_URL, {
        withCredentials: true,
      });

      socket.on('connect', () => {
        store.dispatch(chatActions.connectionEstablished());
        socket.emit(ChatEvent.RequestAllMessages);
      })

      socket.on(ChatEvent.SendAllMessages, (messages: ChatMessage[]) => {
        store.dispatch(chatActions.receiveAllMessages({ messages }));
      })

      socket.on(ChatEvent.ReceiveMessage, (message: ChatMessage) => {
        store.dispatch(chatActions.receiveMessage({ message }));
      })
    }

    if (chatActions.submitMessage.match(action) && isConnectionEstablished) {
      socket.emit(ChatEvent.SendMessage, action.payload.content);
    }

    next(action);
  }
}

export default chatMiddleware;

In the above code, we do the following:

  • Emit theChatEvent.RequestAllMessagesevent as soon as the application establishes the connection.
  • Listen to theChatEvent.SendAllMessagesevent that the backend emits in response to theChatEvent.RequestAllMessagesevent.
  • Emit theChatEvent.SendMessageevent when the user dispatches thechatActions.submitMessageaction.
  • Listen to theChatEvent.ReceiveMessageevent the backend emits when any of the users send a message.

Summary

In this article, we’ve explained what the middleware is in the context of Redux. When doing that, we’ve used Redux Toolkit and TypeScript. Since a middleware exists for the whole lifetime of our application, it is a good place to connect to the WebSocket. A middleware also has access to the current state and can dispatch actions. It can react to incoming messages and emit them when the user dispatches an action.

Original article sourced at: https://wanago.io

#websocket #redux 

Jesus  Moran

Jesus Moran

1622824320

Modern Redux with Redux Toolkit

Redux Toolkit is the official, opinionated, batteries-included toolset for efficient Redux development. Mark Erikson (@acmemarke), long-time Redux maintainer and avid blogger about all things web development showed us the potential of Redux in action with an awesome demo!

Some handy links you might encounter in the video:
➡️ https://blog.isquaredsoftware.com/2021/01/context-redux-differences/
➡️ https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation/
➡️ https://github.com/immerjs/immer

  • 00:00 - Intro
  • 00:25 - Meet Mark Erikson
  • 02:57 - Is Redux dead?
  • 06:25 - Redux is a jack of all trades
  • 09:00 - What makes the Modern Redux tick? v7.1, Hooks
  • 10:43 - useSelector hook
  • 11:31 - useDispatch
  • 13:23 - What is Redux ToolKit & what does it do?
  • 15:30 - configureStore
  • 17:00 - Immer
  • 18:25 - createReducer API
  • 19:19 - createAction
  • 19:57 - createSlice
  • 23:27 - createSelector
  • 23:40 - createAsyncThunk
  • 24:40 - createEntityAdapter
  • 26:43 - Redux Toolkit safety check
  • 28:20 - Redux Toolkit: RTK Query
  • 32:57 - App Setup
  • 34:05 - App Usage
  • 35:05 - Redux Templates for Create-React-App
  • 35:40 - Coding demo time! - Redux + TypeScrypt + Vite App Example
  • 47:28 - RTK Query Overview
  • 50:05 - New “Redux Essential” Tutorial
  • 51:35 - Outro

React All-Day is a long-format stream of fun and learning with React experts, and live coding from familiar names and faces from around the React world!

Eight awesome guests covered eight exciting topics from sessions on testing, data management, full-stack frameworks to programming concepts, and more.

React Wednesdays is a weekly chat show with the best and brightest from the React world. Join us live every Wednesdays to hang out and ask questions. Learn more about the show and upcoming episodes at https://www.telerik.com/react-wednesdays.

#redux #redux

Что такое Redux Middleware и как его использовать с WebSockets

В этой статье вы узнаете, что такое промежуточное ПО Redux и как его использовать с WebSockets. Промежуточное ПО — отличное место для подключения к WebSockets, если вы используете Redux. 

мы также много говорим о различных серверных технологиях, таких как Express. Одним из распространенных понятий является промежуточное ПО. В редуксе он служит той же цели. Если вы хотите узнать больше об Express, ознакомьтесь с учебником TypeScript Express #1. Промежуточное ПО, маршрутизация и контроллеры

Здесь мы используем Redux Toolkit с TypeScript. Чтобы получить ту же начальную модель, запустите

npx create-react-app my-app --template redux-typescript.

Идея промежуточного программного обеспечения Redux

Промежуточное ПО позволяет добавить логику между моментом, когда пользователь отправляет действие, и моментом, когда оно достигает редюсера. Одним из самых простых вариантов использования промежуточного программного обеспечения является ведение журнала.

Что-то, что может немного сбить с толку, это то, что в Redux промежуточное ПО использует каррирование . Это метод, при котором функция, принимающая несколько аргументов, разбивается на цепочку функций, каждая из которых принимает только один аргумент. Дэн Абрамов оправдывает это тем, что мы можем захотеть использовать замыкания JavaScript в своих интересах. Хотя не все согласны с этим дизайнерским решением , возможно, не стоит вносить существенные изменения, чтобы исправить это на данном этапе.

Чтобы понять это, давайте создадим простой пример.

loggerMiddleware.tsx

import { Middleware } from 'redux'

const loggerMiddleware: Middleware = (store) => {
  return (next) => {
    return (action) => {
      console.log('dispatching', action);
      const result = next(action);
      console.log('next state', store.getState());
      return result;
    }
  }
}

export default loggerMiddleware;

 

Мы могли бы написать приведенный выше код в более короткой форме, но пока давайте будем очень явными.

Когда мы добавляем вышеуказанное промежуточное ПО в наш магазин, это приводит к тому, что все действия, которые мы отправляем, записываются.

Потоковое ПО промежуточного слоя

nextargument — это самое важное, что нужно понять в приведенном выше коде. Поскольку у нас может быть несколько промежуточных программ, следующая функция вызывает следующую в цепочке. Если промежуточного программного обеспечения не осталось, оно отправляет действие.

 

Если бы мы не позвонили

nextfunction действие не будет отправлено.

 

Давайте добавим еще один пример из официальной документации, чтобы лучше его представить.

крашMiddleware.tsx

import { Middleware } from 'redux'

const crashMiddleware: Middleware = store => next => action => {
  console.log('crashMiddleware');
  try {
    return next(action)
  } catch (error) {
    console.error('Caught an exception!', error)
    throw error;
  }
}

export default crashMiddleware;

В этой статье мы используем Redux Toolkit . Он сразу же добавляет промежуточное ПО в наш магазин. Чтобы добавить новое промежуточное ПО при сохранении значений по умолчанию, мы можем использовать

getDefaultMiddleware, как предлагает документация .

store.tsx

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import loggerMiddleware from './loggerMiddleware';
import crashMiddleware from './crashMiddleware';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
  middleware: (getDefaultMiddleware) => {
    return getDefaultMiddleware().concat([crashMiddleware, loggerMiddleware])
  },
});

 

О

counterReducer поставляется в комплекте с шаблоном redux-typescript, упомянутым в начале этой статьи.

 

Благодаря тому, что мы добавили

CrashMiddleware перед loggerMiddleware, он запускается первым.

 

Если бы мы не позвонили

nextfunction в crashMiddleware, loggerMiddleware никогда не запустится и действие не будет отправлено.

 

Промежуточное ПО Usando WebSockets

В API с NestJS #26. Живой чат с WebSockets , мы создали серверную часть для приложения чата, используя socket.io. Мы видим, что есть некоторые события, которые могут произойти. Давайте создадим перечисление, содержащее их все:

Хотя в этой статье мы используем socket.io, он может быть очень похож на простые веб-сокеты.

chatEvent.tsx

enum ChatEvent {
  SendMessage = 'send_message',
  RequestAllMessages = 'request_all_messages',
  SendAllMessages = 'send_all_messages',
  ReceiveMessage = 'receive_message'
}

export default ChatEvent;

 

установление соединения

Чтобы установить соединение, нам нужен URL-адрес нашего серверного приложения. Хороший способ сохранить его во внешнем интерфейсе — использовать переменные среды.

.env

REACT_APP_API_URL=http://localhost:3000

реакция-приложение-env.d.ts

/// 

namespace NodeJS {
  interface ProcessEnv {
    REACT_APP_API_URL: string;
  }
}

Давайте также определим основы нашего среза с помощью основных действий.

чатSlice.tsx

import { createSlice } from '@reduxjs/toolkit';

export interface ChatState {
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

Последней частью установления соединения является создание промежуточного программного обеспечения. API, который мы создали в этой статье, требует аутентификации с помощью файлов cookie. Чтобы справиться с этим, нам понадобится

withCredentialargument.

чатMiddleware.tsx

import { Middleware } from 'redux'
import { io } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';

const chatMiddleware: Middleware = store => next => action => {
  if (!chatActions.startConnecting.match(action)) {
    return next(action);
  }

  const socket = io(process.env.REACT_APP_API_URL, {
    withCredentials: true,
  });

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

  next(action);
}

export default chatMiddleware;

вызов

Функция chatActions.startConnecting.match — это то, как мы можем проверить, соответствует ли действие действию, определенному в слайсе.

 

Наше приложение устанавливает соединение через WebSockets, когда пользователь отправляет

начать действие подключения.

Отправка и получение сообщений чата

Все сообщения из нашего бэкенда имеют определенную структуру данных. Итак, давайте создадим соответствующий ему интерфейс.

чатСообщение.tsx

interface ChatMessage {
  id: number;
  content: string;
  author: {
    email: string;
  }
}

export default ChatMessage;

Теперь, когда у нас есть вышеуказанный интерфейс, мы можем использовать его в нашем фрагменте.

чатSlice.tsx

import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import ChatMessage from "./chatMessage";

export interface ChatState {
  messages: ChatMessage[];
  isEstablishingConnection: boolean;
  isConnected: boolean;
}

const initialState: ChatState = {
  messages: [],
  isEstablishingConnection: false,
  isConnected: false
};

const chatSlice = createSlice({
  name: 'chat',
  initialState,
  reducers: {
    startConnecting: (state => {
      state.isEstablishingConnection = true;
    }),
    connectionEstablished: (state => {
      state.isConnected = true;
      state.isEstablishingConnection = true;
    }),
    receiveAllMessages: ((state, action: PayloadAction<{
      messages: ChatMessage[]
    }>) => {
      state.messages = action.payload.messages;
    }),
    receiveMessage: ((state, action: PayloadAction<{
      message: ChatMessage
    }>) => {
      state.messages.push(action.payload.message);
    }),
    submitMessage: ((state, action: PayloadAction<{
      content: string
    }>) => {
      return;
    })
  },
});

export const chatActions = chatSlice.actions;

export default chatSlice;

Последней частью отправки и получения сообщений является прослушивание всех событий и их отправка.

чатMiddleware.tsx

import { Middleware } from 'redux'
import { io, Socket } from 'socket.io-client';
import { chatActions } from './chatSlice';
import ChatEvent from './chatEvent';
import ChatMessage from "./chatMessage";

const chatMiddleware: Middleware = store => {
  let socket: Socket;

  return next => action => {
    const isConnectionEstablished = socket && store.getState().chat.isConnected;

    if (chatActions.startConnecting.match(action)) {
      socket = io(process.env.REACT_APP_API_URL, {
        withCredentials: true,
      });

      socket.on('connect', () => {
        store.dispatch(chatActions.connectionEstablished());
        socket.emit(ChatEvent.RequestAllMessages);
      })

      socket.on(ChatEvent.SendAllMessages, (messages: ChatMessage[]) => {
        store.dispatch(chatActions.receiveAllMessages({ messages }));
      })

      socket.on(ChatEvent.ReceiveMessage, (message: ChatMessage) => {
        store.dispatch(chatActions.receiveMessage({ message }));
      })
    }

    if (chatActions.submitMessage.match(action) && isConnectionEstablished) {
      socket.emit(ChatEvent.SendMessage, action.payload.content);
    }

    next(action);
  }
}

export default chatMiddleware;

В приведенном выше коде мы делаем следующее:

  • Создайте событие ChatEvent.RequestAllMessage, как только приложение установит соединение.
  • Прослушайте событие ChatEvent.SendAllMessage, которое серверная часть генерирует в ответ на событие ChatEvent.RequestAllMessage.
  • Генерирует событие ChatEvent.SendMessage, когда пользователь отправляет действие chatActions.submitMessage.
  • Прослушайте событие ChatEvent.ReceiveMessage, которое серверная часть генерирует, когда любой из пользователей отправляет сообщение.

Краткое содержание

В этой статье мы объясним, что такое промежуточное ПО в контексте Redux. При этом мы используем Redux Toolkit и TypeScript. Поскольку промежуточное ПО существует на протяжении всего жизненного цикла нашего приложения, это хорошее место для подключения к WebSocket. Промежуточное ПО также имеет доступ к текущему состоянию и может отправлять действия. Он может реагировать на входящие сообщения и выдавать их, когда пользователь отправляет действие.

Оригинальная статья получена с: https://wanago.io

#websocket #redux