Debouncing with Redux Middleware

Introduction

A common conundrum in today’s front-end framework world is knowing when and how to take certain asynchronous actions, such as persisting data to a backend. If we’re using a state management library like Redux, we might be further confused as to where without our Redux code we might put this logic.

A Concrete Scenario

For the purposes of this blog post, let’s assume we are using React with Redux and want to periodically save our state data to a backend. We have elected to use debouncing to do this, meaning we’d like to perform the save action after our state hasn’t changed for a certain amount of time.

Considering Our Options

So, what are our options when using React with Redux? I think the following list covers it:

  • Do it in a component - Have a component that subscribes to our state and, when it renders, do the debouncing/saving.
  • Do it in a redux action creator - Using something like thunk middleware, trigger the debounce function in an action create prior to dispatching the associated action.
  • Do it in a reducer - As you update your site data in the reducer, call a debounce function. (See note below for why I think this option is bad).
  • Do it in Redux middleware - Create a middleware that runs the debounce function anytime your state changes.

Note: I think all of these are actually legitimate ways except performing the save in a reducer. Reducers really should be pure functions and performing data fetching from within the reducer is a side effect.

I Like the Middleware Approach

As I mentioned above, I think most of these approaches could work fine, but I especially like the middleware approach. It nicely isolates your saving code, can selectively define which actions cause saving to start, doesn’t require installing thunk middleware if you’re not already using it, and doesn’t require you to include a component that exists only to handle saving.

The Implementation

First, we can create a saveDebounce function that will be called by our middleware. To implement debouncing, we’ll make use of setTimeout and clearTimeout.

let saveTimer;
let debounceTime = 10000; // 10 seconds

const saveDebounce = data => {
  if (saveTimer) {
    clearTimeout(saveTimer);
  }

  saveTimer = setTimeout(() => {
    // Use request library of choice here
    fetch('my-api-endpoint', {
      method: 'POST',
      body: JSON.stringify(data),
    });
  }, debounceTime);
};

Next, the actual middleware, which is pretty simple.

export const dataSaver = store => next => action => {
  saveDebounce(store.getState());
  return next(action);
};

As a user is modifying state, the saveDebounce function will clear any previous timeout and start a new one. Only when the user hasn’t changed state for 10 seconds will our fetch actually be called.

Finally, we need to register our middleware with Redux. This is done when we create our store.

#redux

What is GEEK

Buddha Community

Debouncing with Redux Middleware

Redux Toolkit's new listener middleware vs. Redux-Saga

Redux is one of the most popular state management libraries in JavaScript, especially among React developers. It makes managing complex UI states easier. Redux reducers, the main building blocks of Redux, are pure functions by design. You initiate state updates by dispatching simple synchronous actions with plain vanilla Redux.

However, real-world applications need to perform more than just simple action dispatches. You need to enhance Redux’s capability using middleware libraries like Redux Thunk, Redux-Saga, and the recently released listener middleware to manage side effects and more complex synchronous and asynchronous processes.

Since Redux Toolkit (RTK) became the de facto toolset for writing modern Redux code, it is accurate to say that Redux Thunk also became the default middleware, because it is part of RTK by default. However, you can use a different middleware library if the default doesn’t meet your use case.

Despite its simplicity, thunks have limitations. One of the most cited limitations is the inability to run code in response to dispatched actions or state updates. Doing so requires writing custom middleware or using more powerful middleware libraries like Redux-Saga. Thunks are also relatively difficult to test.

The reason for creating the new listener middleware is to fill that void. In this article, I will compare the new listener middleware with Redux-Saga and highlight some of the cross-cutting features. Before doing so, let me introduce the listener middleware and Redux-Saga in the following sections.

Introduction to Redux Toolkit’s new listener middleware

As mentioned above, the Redux maintainers mooted the new listener middleware functionality to enhance the capability of RTK and offer an in-house solution to most of the use cases covered by Sagas. It has finally landed in RTK v1.8.0 after endless iterations. The middleware’s primary functionality is let users respond to dispatched actions, or run code in response to state updates.

According to the maintainers, the listener middleware is conceptually similar to React’s useEffect hook. The useEffect hook is for running side effects in React functional components. It runs immediately on component mount and on subsequent re-renders when one of its dependencies has changed.

Similarly, you have absolute control over when a listener runs. You can register a listener to run when some actions are dispatched, on every state update, or after meeting certain conditions. To use the listener middleware, import the createListenerMiddleware function like any other RTK functionality. It is available in RTK v1.8.0 or later.

import { configureStore, createListenerMiddleware } from "@reduxjs/toolkit";
const listenerMiddleware = createListenerMiddleware();

You can add listeners to the middleware statically during setup or add and remove them dynamically at runtime. To add it statically at setup, you need to invoke the startListening method of the middleware instance like so:

listenerMiddleware.startListening({
  actionCreator: addTodo,
  effect: async (action, listenerApi) => {
    console.log(listenerApi.getOriginalState());
    console.log(action);
    await listenerApi.delay(5000);
    console.log(listenerApi.getState());
  },
});

The effect callback will run after dispatching the specified action in the example above. The effect callback takes two parameters by default: the dispatched action, and the listener API object. The listener API object has functions such as dispatch and getState for interacting with the Redux store.

In the listener’s effect callback, you can perform side effects, cancel running listener instances, spawn child processes, dispatch actions, and access the state.

If you want to remove or add the listener dynamically at runtime, you can dispatch standard built-in actions. When registering the listener callback, you can specify when it will run strictly using one of the following properties:

  • Action type: the type property is the exact action type string that will trigger the effect callback
  • Action creator: the actionCreator property is the exact action creator that triggers the effect callback
  • Matcher: the matcher property matches one of many actions using RTK matcher and triggers the effect callback when there is a match
  • Predicate: the predicate property is a function that returns true or false to determine whether the effect callback should run or not. It has access to the dispatched action and current and previous states

The properties outlined above belong to the object you pass to the startListening function when adding a listener to the middleware.

With RTK, you can add the listener middleware like any other middleware:

export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleWare) => {
    return getDefaultMiddleWare({ thunk: false }).prepend(listenerMiddleware);
  }
});

What is Redux-Saga?

Redux-Saga is one of the popular middleware libraries for Redux. It makes working with asynchronous operations and side effects a lot easier. Unlike other libraries like Redux Thunk, Redux-Saga uses ES6 generators. Therefore, you should be knowledgeable in ES6 generators to implement Sagas correctly.

Generator functions

You can declare a generator function using the function* construct. Below is a basic example of a generator function. If it looks unfamiliar to you, follow the link in the opening paragraph to understand iterators and generators before continuing:

function* countToThree() {
  yield 1;
  yield 2;
  yield 3;
}

const counter = countToThree();

console.log(counter.next()); // {value: 1, done: false}
console.log(counter.next()); // {value: 2, done: false}
console.log(counter.next()); // {value: 3, done: false}

Invoking a generator function doesn’t execute the function body like regular functions do. It instead returns an iterator object with the next method for executing the function body.

Invoking the next method will execute the function body until it encounters the first yield keyword. It pauses execution and returns an object with the properties value and done. The value property holds the value yielded, and done is a boolean specifying whether all the values have been yielded.

Invoking next again will resume function execution until it encounters the next yield. It again pauses execution and returns an object with the properties value and done like before. This process continues as you continue invoking next.

Understanding Sagas

A typical Redux-Saga middleware setup has watcher Sagas and worker Sagas. The watcher Sagas are generator functions that watch for dispatched actions. Worker Sagas are generator functions you yield from watcher Sagas and are usually responsible for performing side effects.

The code below is a simple illustration of how you can implement watcher and worker Sagas:

const fetchTodo = (url) => fetch(url).then((res) => res.json());

function* workerSaga(action) {
  const { url } = action.payload;
  try {
    const todo = yield call(fetchTodo, url);
    yield put(addTodo(todo));
  } catch (error) {
    yield put(setError({ error }));
  }
};

function* watcherSaga() {
  yield takeEvery(fetchTodo.toString(), workerSaga);
};

The functions call, put, and takeEvery are helper effects and are part of the Redux-Saga API. Check the documentation for more on how they work.

The watcher``Saga Generator function runs for every dispatch of the specified action. In the worker Saga, you can run side effects, access state, dispatch actions, and cancel running processes.

If you are using Redux-Saga with RTK, you can add it to the middleware list like any other middleware:

const SagaMiddleware = createSagaMiddleware();

export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => {
    return getDefaultMiddleware({ thunk: false }).prepend(SagaMiddleware);
  },
});

SagaMiddleware.run(rootSaga);

Comparing the new listener middleware with Redux-Saga

The previous sections introduced you to the listener middleware and Redux-Saga. As pointed out, the listener middleware covers most of the primary Redux-Saga use cases. We shall compare some of the functionalities in the listener middleware and Redux-Saga in this section.

Before we get started, it is worth mentioning that the listener middleware’s effect callback runs after invoking the root reducer and updating state. Therefore, if your goal is to strictly update state from the effects callback, dispatch an action that will trigger the effect without updating the state. After that, you can run some side effect logic and dispatch another action to update state from within the effect callback.

Delaying effect execution

With the new listener middleware, it is possible to pause or delay the execution of the effect callback. The delay function delays code execution within the effect callback for a specific duration and resumes after that. It takes the number of milliseconds as an argument and returns a Promise which resolves after the specified milliseconds.

The delay function is part of the listener API object. You can use it like so in the listener middleware:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    const { todoId } = action.payload;
    const todo = await api.fetchTodo(todoId);
    await listenerApi.delay(500);
    listenerApi.dispatch(addTodo(todo));
  },
});

Redux-Saga also has a delay function, similar to the delay function of the listener middleware. It takes the number of milliseconds as an argument and delays for the specified duration.

Below is the equivalent implementation of the above functionality in Redux-Saga:

function* fetchTodo(action){
  const { todoId } = action.payload;
  const todo = yield api.fetchTodo(todoId);
  yield delay(500);
  yield put(addTodo(todo));
}

Debouncing

The listener middleware doesn’t have built-in functionality for debouncing like Redux-Saga. However, you can use functions such as cancelActiveListeners and delay to implement similar functionality. They are part of the listener API object.

Invoking cancelActiveListeners will cancel all other running instances of a listener except the one that invoked it. You can then delay execution for a specific duration. The latest listener instance will run to completion when there isn’t any related action dispatch or state update during the delay:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    listenerApi.cancelActiveListeners();
    await listenerApi.delay(500);
  },
});

The above listener middleware implementation is similar to the built-in debounce function in Redux-Saga:

function* watcherSaga() {
  yield debounce(500, fetchTodo.toString(), workerSaga);
}

Throttling

Like debouncing, the listener middleware doesn’t have a built-in function for throttling like Redux-Saga. However, you can use the subscribe, delay, and unsubscribe functions of the listener API object to implement similar functionality. Unsubscribing a listener will remove it from the list of listeners.

You can then use the delay function to delay execution for a specific duration. During the delay, the middleware will ignore all action dispatches or state updates that are supposed to trigger the effect callback. You can re-subscribe the listener after that:

listener.startListening({
  type: fetchTodo.toString(),
  effect: async (action, listenerApi) => {
    listenerApi.unsubscribe();
    console.log('Original state ', listenerApi.getOriginalState());
    await listenerApi.delay(1000);
    console.log('Current state ', listenerApi.getState());
    listenerApi.subscribe();
  }
});

You need to call getOriginalState synchronously otherwise it will throw an error.

The above implementation in the listener middleware is similar to the built-in throttle function in Redux-Saga:

function* watcherSaga() {  
  yield throttle(1000, fetchTodo.toString(), workerSaga)  
}

Watching every action dispatch

In the introduction to the new listener middleware section, I mentioned that you can specify when the effect callback will trigger in one of four ways. You can use either the action type, actionCreator, matcher, or predicate property of the object you pass to the startListening function.

The predicate is a function that has access to the dispatched action, the previous, and the current states. The effect callback runs if the predicate returns true. Therefore, if it always returns true, as in the example below, the effect callback runs on every action dispatch or state update:

listenerMiddleware.startListening({
  predicate: (action, currState, prevState) => true,
  effect: async (action, listenerApi) => {
    console.log('Previous state ', listenerApi.getOriginalState());
    console.log('Current state ', listenerApi.getstate());
  },
});

The above functionality in the listener middleware is similar to Redux-Saga’s takeEvery helper effect with the * wildcard character. Using takeEvery with * watches for every incoming action dispatch regardless of its type, and then spawns a new child task. The difference is that the listener middleware runs its effect callback after state update:

function* watchEveyDispatchAndLog(){
  yield takeEvery('*', logger);
}

Creating a one-off listener

If you want to create a one-shot listener with the new listener middleware, you can use the unsubscribe function to remove the listener from the middleware after running some code. Therefore, future dispatches of the same action won’t trigger the effect callback:

listenerMiddleware.startListening({ 
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    console.log(action);
    listenerApi.unsubscribe();
  },
});

However, note that the unsubscribe function will not cancel already running instances of the effect callback. You can cancel running instances using the cancelActiveListeners function before unsubscribing.

The above functionality is equivalent to using the take helper effect to specify which action dispatch to watch in Redux-Saga:

function* watchIncrementVisitCount(){
    yield take(incrementVisitCount());
    yield api.incrementVisitCount();
}

The above Saga will strictly take the first dispatch of the specified action and stop watching after that. Though the above example only takes the first dispatch, you can modify it to watch as many dispatches as you want.

Launching child tasks

It is possible to launch child tasks in the listener callback using the listener API’s fork function. The function fork takes an asynchronous or synchronous function as an argument. You can use it to execute additional tasks within the effect callback:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    const task = listenerApi.fork(async (forkApi) => {
    });
    const result = await task.result;
  },
});

The above listener middleware functionality is similar to running additional tasks in Redux-Saga with either the fork or spawn helper effect. The fork effect creates attached task while spawn creates detached task:

function* fetchTodos() {
  const todo1 = yield fork(fetchTodo, '1');
  const todo2 = yield fork(fetchTodo, '2');
}

Canceling running listener instances

For multiple running instances of the same listener, the listener middleware provides the cancelActiveListeners utility function for canceling the other instances in the effect callback. As a result, the callback runs for the latest dispatch:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerMiddlewareApi) => {
    listenerMiddlewareApi.cancelActiveListeners();
     },
});

The above functionality of the listener middleware is similar to Redux-Saga’s takeLatest effect creator. The takeLatest effect creator also cancels previously started Saga tasks, if they are still running, in favor of the latest one:

function* watchFetchTodo() {
  yield takeLatest(addTodo.toString(), fetchTodo);
};

Bundle size

The bundle size of the listener middleware is approximately half that of Redux-Saga. The table below shows the bundle sizes for Redux-Saga and the listener middleware obtained from bundlephobia.

I have also included RTK in the table below because it is the recommended toolset for working with Redux. The listener middleware is bundled with RTK by default. Though RTK is relatively large, it simplifies working with Redux.

PackageMinified sizeMinified + Gzipped size
Redux-Saga14kB5.3kB
Listener middleware6.6kB2.5kB
Redux Toolkit (RTK)39.3kB12.7kB

Learning curve

Despite being powerful, one of the most cited downsides of using Redux-Saga is its steep learning curve, especially if you are unfamiliar with generators and Sagas.

Unlike Redux-Saga, the new listener middleware exposes a minimal set of functionalities you can learn very fast. You can then use them flexibly to replicate some of the common Redux-Saga use cases as illustrated in the previous sub-sections.

Testing

One of the benefits of using Redux-Saga over its contemporaries like Thunks is that Redux-Saga’s generator functions and the built-in helper effects make testing some of the common patterns straightforward.

Like Redux-Saga, it is easy to test some of the common patterns of the new listener middleware, and there are great examples of how to test some of the common patterns in the documentation.

Conclusion

Overall, the new listener middleware is a simpler and lightweight alternative to Redux-Saga., and picking it up is straightforward. Unless you maintain a codebase that relies heavily on Redux-Saga, it is worth exploring. If it doesn’t meet your use case, you can use Redux-Saga or another middleware with great results.

Though the listener middleware is the missing functionality for RTK to offer in-house solutions to most of the problems Redux-Saga seems to solve, it doesn’t mean that it covers all use cases.

It is also worth mentioning that this article made a basic comparison. We may not know the true power or limitations of the listener middleware until it has been used extensively in production, even though many devs are already using it creatively to solve problems.

If there is anything I have missed, do let me know in the comments section below.

Source: https://blog.logrocket.com/redux-toolkits-new-listener-middleware-vs-redux-saga/

#middleware #redux 

El Nuevo Middleware De Escucha De Redux toolkit Frente A Redux-Saga

Redux es una de las bibliotecas de administración de estado más populares en JavaScript, especialmente entre los desarrolladores de React. Facilita la gestión de estados de interfaz de usuario complejos. Los reductores de Redux, los principales bloques de construcción de Redux, son funciones puras por diseño. Usted inicia las actualizaciones de estado mediante el envío de acciones sincrónicas simples con Redux normal y corriente.

Sin embargo, las aplicaciones del mundo real necesitan realizar más que simples envíos de acciones. Debe mejorar la capacidad de Redux mediante el uso de bibliotecas de middleware como Redux Thunk , Redux-Saga y el middleware de escucha lanzado recientemente para administrar los efectos secundarios y los procesos sincrónicos y asincrónicos más complejos.

Dado que Redux Toolkit (RTK) se convirtió en el conjunto de herramientas de facto para escribir código Redux moderno, es correcto decir que Redux Thunk también se convirtió en el middleware predeterminado, porque es parte de RTK de manera predeterminada. Sin embargo, puede usar una biblioteca de middleware diferente si el valor predeterminado no se ajusta a su caso de uso.

A pesar de su simplicidad, los thunks tienen limitaciones. Una de las limitaciones más citadas es la incapacidad de ejecutar código en respuesta a acciones enviadas o actualizaciones de estado. Hacerlo requiere escribir middleware personalizado o usar bibliotecas de middleware más potentes como Redux-Saga. Los thunks también son relativamente difíciles de probar.

La razón para crear el nuevo middleware de escucha es llenar ese vacío. En este artículo, compararé el nuevo middleware de oyentes con Redux-Saga y resaltaré algunas de las características transversales. Antes de hacerlo, permítanme presentarles el middleware de escucha y Redux-Saga en las siguientes secciones.

Introducción al nuevo middleware de escucha de Redux Toolkit

Como se mencionó anteriormente, los mantenedores de Redux discutieron la nueva funcionalidad de middleware de escucha para mejorar la capacidad de RTK y ofrecer una solución interna para la mayoría de los casos de uso cubiertos por Sagas. Finalmente llegó a RTK v1.8.0 después de interminables iteraciones. La funcionalidad principal del middleware es permitir que los usuarios respondan a acciones enviadas o ejecuten código en respuesta a actualizaciones de estado.

Según los mantenedores, el middleware de escucha es conceptualmente similar al useEffectgancho de React. El useEffectgancho es para ejecutar efectos secundarios en componentes funcionales de React. Se ejecuta inmediatamente en el montaje del componente y en los renderizados posteriores cuando una de sus dependencias ha cambiado.

Del mismo modo, tiene control absoluto sobre cuándo se ejecuta un oyente. Puede registrar un oyente para que se ejecute cuando se envían algunas acciones, en cada actualización de estado o después de cumplir ciertas condiciones. Para usar el middleware de escucha, importe la createListenerMiddlewarefunción como cualquier otra funcionalidad RTK. Está disponible en RTK v1.8.0 o posterior.

import { configureStore, createListenerMiddleware } from "@reduxjs/toolkit";
const listenerMiddleware = createListenerMiddleware();

Puede agregar oyentes al middleware de forma estática durante la configuración o agregarlos y eliminarlos de forma dinámica durante el tiempo de ejecución. Para agregarlo estáticamente en la configuración, debe invocar el startListeningmétodo de la instancia de middleware de la siguiente manera:

listenerMiddleware.startListening({
  actionCreator: addTodo,
  effect: async (action, listenerApi) => {
    console.log(listenerApi.getOriginalState());
    console.log(action);
    await listenerApi.delay(5000);
    console.log(listenerApi.getState());
  },
});

La effectdevolución de llamada se ejecutará después de enviar la acción especificada en el ejemplo anterior. La effectdevolución de llamada toma dos parámetros de forma predeterminada: la acción enviada y el objeto API de escucha. El objeto API de escucha tiene funciones como dispatchy getStatepara interactuar con la tienda Redux.

En la effectdevolución de llamada del oyente, puede realizar efectos secundarios, cancelar instancias de oyentes en ejecución, generar procesos secundarios, enviar acciones y acceder al estado.

Si desea eliminar o agregar el oyente dinámicamente en tiempo de ejecución, puede enviar acciones integradas estándar. Al registrar la devolución de llamada del oyente, puede especificar cuándo se ejecutará estrictamente usando una de las siguientes propiedades:

  • Tipo de acción: la typepropiedad es la cadena de tipo de acción exacta que activará la effectdevolución de llamada
  • Creador de acción: la actionCreatorpropiedad es el creador de acción exacto que activa la effectdevolución de llamada
  • Comparador: la matcherpropiedad coincide con una de las muchas acciones que utilizan el comparador RTK y activa la effectdevolución de llamada cuando hay una coincidencia
  • Predicado: la predicatepropiedad es una función que devuelve trueo falsepara determinar si la effectdevolución de llamada debe ejecutarse o no. Tiene acceso a la acción despachada y estados actuales y anteriores

Las propiedades descritas anteriormente pertenecen al objeto que pasa a la startListeningfunción al agregar un oyente al middleware.

Con RTK, puede agregar el middleware de escucha como cualquier otro middleware:

export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleWare) => {
    return getDefaultMiddleWare({ thunk: false }).prepend(listenerMiddleware);
  }
});

¿Qué es Redux-Saga?

Redux-Saga es una de las bibliotecas de middleware populares para Redux. Hace que trabajar con operaciones asincrónicas y efectos secundarios sea mucho más fácil. A diferencia de otras bibliotecas como Redux Thunk, Redux-Saga utiliza generadores ES6 . Por lo tanto, debe tener conocimientos en generadores ES6 para implementar Sagas correctamente.

Funciones del generador

Puede declarar una función de generador utilizando la function*construcción. A continuación se muestra un ejemplo básico de una función de generador. Si no le resulta familiar, siga el enlace en el párrafo inicial para comprender los iteradores y generadores antes de continuar:

function* countToThree() {
  yield 1;
  yield 2;
  yield 3;
}

const counter = countToThree();

console.log(counter.next()); // {value: 1, done: false}
console.log(counter.next()); // {value: 2, done: false}
console.log(counter.next()); // {value: 3, done: false}

Invocar una función de generador no ejecuta el cuerpo de la función como lo hacen las funciones regulares. En su lugar, devuelve un objeto iterador con el nextmétodo para ejecutar el cuerpo de la función.

Invocar el nextmétodo ejecutará el cuerpo de la función hasta que encuentre la primera yieldpalabra clave. Detiene la ejecución y devuelve un objeto con las propiedades valuey done. La valuepropiedad contiene el valor obtenido y donees un valor booleano que especifica si se han obtenido todos los valores.

Invocar nextde nuevo reanudará la ejecución de la función hasta que encuentre el siguiente yield. De nuevo pausa la ejecución y devuelve un objeto con las propiedades valuey donecomo antes. Este proceso continúa a medida que continúas invocando next.

Comprender las sagas

Una configuración típica de middleware de Redux-Saga tiene Sagas de observador y Sagas de trabajador. Las Sagas del observador son funciones generadoras que vigilan las acciones despachadas. Worker Sagas son funciones generadoras que obtienes de Watcher Sagas y generalmente son responsables de realizar efectos secundarios.

El siguiente código es una ilustración simple de cómo puede implementar Sagas de observador y trabajador:

const fetchTodo = (url) => fetch(url).then((res) => res.json());

function* workerSaga(action) {
  const { url } = action.payload;
  try {
    const todo = yield call(fetchTodo, url);
    yield put(addTodo(todo));
  } catch (error) {
    yield put(setError({ error }));
  }
};

function* watcherSaga() {
  yield takeEvery(fetchTodo.toString(), workerSaga);
};

Las funciones call, puty takeEveryson efectos auxiliares y forman parte de la API de Redux-Saga. Consulte la documentación para obtener más información sobre cómo funcionan.

La watcher``Sagafunción Generador se ejecuta para cada una dispatchde las acciones especificadas. En Worker Saga, puede ejecutar efectos secundarios, acceder al estado, enviar acciones y cancelar procesos en ejecución.

Si está utilizando Redux-Saga con RTK, puede agregarlo a la lista de middleware como cualquier otro middleware:

const SagaMiddleware = createSagaMiddleware();

export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => {
    return getDefaultMiddleware({ thunk: false }).prepend(SagaMiddleware);
  },
});

SagaMiddleware.run(rootSaga);

Comparando el nuevo middleware de escucha con Redux-Saga

Las secciones anteriores le presentaron el middleware de escucha y Redux-Saga. Como se señaló, el middleware de escucha cubre la mayoría de los casos de uso principales de Redux-Saga. Compararemos algunas de las funcionalidades en el middleware de escucha y Redux-Saga en esta sección.

Antes de comenzar, vale la pena mencionar que la effectdevolución de llamada del middleware de escucha se ejecuta después de invocar el reductor raíz y actualizar el estado. Por lo tanto, si su objetivo es actualizar estrictamente el estado desde la effectsdevolución de llamada, envíe una acción que active el effectsin actualizar el estado. Después de eso, puede ejecutar alguna lógica de efectos secundarios y enviar otra acción para actualizar el estado desde la effectdevolución de llamada.

Retardar la ejecución del efecto

Con el nuevo middleware de escucha, es posible pausar o retrasar la ejecución de la effectdevolución de llamada. La delayfunción retrasa la ejecución del código dentro de la effectdevolución de llamada durante un tiempo específico y luego se reanuda. Toma el número de milisegundos como argumento y devuelve una Promesa que se resuelve después de los milisegundos especificados.

La delayfunción es parte del objeto API de escucha. Puede usarlo así en el middleware de escucha:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    const { todoId } = action.payload;
    const todo = await api.fetchTodo(todoId);
    await listenerApi.delay(500);
    listenerApi.dispatch(addTodo(todo));
  },
});

Redux-Saga también tiene una delayfunción, similar a la delayfunción del middleware de escucha. Toma la cantidad de milisegundos como argumento y demora la duración especificada.

A continuación se muestra la implementación equivalente de la funcionalidad anterior en Redux-Saga:

function* fetchTodo(action){
  const { todoId } = action.payload;
  const todo = yield api.fetchTodo(todoId);
  yield delay(500);
  yield put(addTodo(todo));
}

antirrebote

El middleware de escucha no tiene una funcionalidad integrada para eliminar rebotes como Redux-Saga. Sin embargo, puede usar funciones como cancelActiveListenersy delaypara implementar una funcionalidad similar. Forman parte del objeto API de escucha.

La invocación cancelActiveListenerscancelará todas las demás instancias en ejecución de un oyente, excepto la que lo invocó. A continuación, puede retrasar la ejecución durante un tiempo específico. La instancia de escucha más reciente se ejecutará hasta completarse cuando no haya ningún envío de acción relacionado o actualización de estado durante el retraso:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    listenerApi.cancelActiveListeners();
    await listenerApi.delay(500);
  },
});

La implementación del middleware de escucha anterior es similar a la debouncefunción integrada en Redux-Saga:

function* watcherSaga() {
  yield debounce(500, fetchTodo.toString(), workerSaga);
}

estrangulamiento

Al igual que la eliminación de rebotes, el middleware de escucha no tiene una función integrada para acelerar como Redux-Saga. Sin embargo, puede usar las funciones subscribe, delayy unsubscribedel objeto API de escucha para implementar una funcionalidad similar. Cancelar la suscripción de un oyente lo eliminará de la lista de oyentes.

A continuación, puede utilizar la delayfunción para retrasar la ejecución durante un tiempo específico. Durante la demora, el middleware ignorará todos los envíos de acciones o actualizaciones de estado que se supone que desencadenarán la effectdevolución de llamada. Puede volver a suscribir al oyente después de eso:

listener.startListening({
  type: fetchTodo.toString(),
  effect: async (action, listenerApi) => {
    listenerApi.unsubscribe();
    console.log('Original state ', listenerApi.getOriginalState());
    await listenerApi.delay(1000);
    console.log('Current state ', listenerApi.getState());
    listenerApi.subscribe();
  }
});

Debe llamar getOriginalStatesincrónicamente; de ​​lo contrario, arrojará un error.

La implementación anterior en el middleware de escucha es similar a la throttlefunción integrada en Redux-Saga:

function* watcherSaga() {  
  yield throttle(1000, fetchTodo.toString(), workerSaga)  
}

Viendo cada acción despachada

En la introducción a la nueva sección de middleware de escucha, mencioné que puede especificar cuándo effectse activará la devolución de llamada de una de cuatro maneras. Puede usar la acción type, actionCreator, matchero predicatela propiedad del objeto que pasa a la startListeningfunción.

El predicado es una función que tiene acceso a la acción despachada, a los estados anterior y actual. La effectdevolución de llamada se ejecuta si el predicado devuelve true. Por lo tanto, si siempre devuelve true, como en el ejemplo a continuación, la effectdevolución de llamada se ejecuta en cada envío de acción o actualización de estado:

listenerMiddleware.startListening({
  predicate: (action, currState, prevState) => true,
  effect: async (action, listenerApi) => {
    console.log('Previous state ', listenerApi.getOriginalState());
    console.log('Current state ', listenerApi.getstate());
  },
});

La funcionalidad anterior en el middleware de escucha es similar al takeEveryefecto auxiliar de Redux-Saga con el *carácter comodín. El uso takeEveryde *relojes para cada envío de acción entrante, independientemente de su tipo, y luego genera una nueva tarea secundaria. La diferencia es que el middleware de escucha ejecuta su effectdevolución de llamada después de la actualización de estado:

function* watchEveyDispatchAndLog(){
  yield takeEvery('*', logger);
}

Creación de un oyente único

Si desea crear un oyente único con el nuevo middleware de oyente, puede usar la unsubscribefunción para eliminar el oyente del middleware después de ejecutar algún código. Por lo tanto, los envíos futuros de la misma acción no activarán la effectdevolución de llamada:

listenerMiddleware.startListening({ 
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    console.log(action);
    listenerApi.unsubscribe();
  },
});

Sin embargo, tenga en cuenta que la unsubscribefunción no cancelará las instancias en ejecución de la effectdevolución de llamada. Puede cancelar las instancias en ejecución utilizando la cancelActiveListenersfunción antes de darse de baja.

La funcionalidad anterior es equivalente a usar el takeefecto de ayuda para especificar qué envío de acción ver en Redux-Saga:

function* watchIncrementVisitCount(){
    yield take(incrementVisitCount());
    yield api.incrementVisitCount();
}

La Saga anterior tomará estrictamente el primer envío de la acción especificada y dejará de mirar después de eso. Aunque el ejemplo anterior solo toma el primer envío, puede modificarlo para ver tantos envíos como desee.

Lanzamiento de tareas secundarias

Es posible iniciar tareas secundarias en la devolución de llamada del oyente utilizando la forkfunción de la API del oyente. La función forktoma una función asíncrona o síncrona como argumento. Puede usarlo para ejecutar tareas adicionales dentro de la effectdevolución de llamada:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerApi) => {
    const task = listenerApi.fork(async (forkApi) => {
    });
    const result = await task.result;
  },
});

La funcionalidad del middleware de escucha anterior es similar a la ejecución de tareas adicionales en Redux-Saga con el efecto de ayuda forko . spawnEl forkefecto crea una tarea adjunta mientras spawncrea una tarea separada:

function* fetchTodos() {
  const todo1 = yield fork(fetchTodo, '1');
  const todo2 = yield fork(fetchTodo, '2');
}

Cancelación de instancias de escucha en ejecución

Para múltiples instancias en ejecución del mismo agente de escucha, el middleware del agente de escucha proporciona la cancelActiveListenersfunción de utilidad para cancelar las otras instancias en la effectdevolución de llamada. Como resultado, la devolución de llamada se ejecuta para el último envío:

listenerMiddleware.startListening({
  actionCreator: fetchTodo,
  effect: async (action, listenerMiddlewareApi) => {
    listenerMiddlewareApi.cancelActiveListeners();
     },
});

La funcionalidad anterior del middleware de escucha es similar al takeLatestcreador de efectos de Redux-Saga. El takeLatestcreador del efecto también cancela las tareas de Saga iniciadas anteriormente, si aún se están ejecutando, a favor de la última:

function* watchFetchTodo() {
  yield takeLatest(addTodo.toString(), fetchTodo);
};

Tamaño del paquete

El tamaño del paquete del middleware de escucha es aproximadamente la mitad del de Redux-Saga. La siguiente tabla muestra los tamaños de paquete para Redux-Saga y el middleware de escucha obtenido de bundlephobia .

También incluí RTK en la siguiente tabla porque es el conjunto de herramientas recomendado para trabajar con Redux. El middleware de escucha se incluye con RTK de forma predeterminada. Aunque RTK es relativamente grande, simplifica el trabajo con Redux.

Paquetetamaño minificadoTamaño minificado + comprimido con Gzip
redux-saga14kB5.3kB
Middleware de escucha6.6kB2.5kB
Kit de herramientas Redux (RTK)39.3kB12.7kB

Curva de aprendizaje

A pesar de ser poderoso, una de las desventajas más citadas de usar Redux-Saga es su pronunciada curva de aprendizaje, especialmente si no está familiarizado con los generadores y Sagas.

A diferencia de Redux-Saga, el nuevo middleware de escucha expone un conjunto mínimo de funcionalidades que puede aprender muy rápido. Luego puede usarlos de manera flexible para replicar algunos de los casos de uso comunes de Redux-Saga como se ilustra en las subsecciones anteriores.

Pruebas

Uno de los beneficios de usar Redux-Saga sobre sus contemporáneos como Thunks es que las funciones del generador de Redux-Saga y los efectos de ayuda incorporados hacen que probar algunos de los patrones comunes sea sencillo.

Al igual que Redux-Saga, es fácil probar algunos de los patrones comunes del nuevo middleware de escucha, y hay excelentes ejemplos de cómo probar algunos de los patrones comunes en la documentación.

Conclusión

En general, el nuevo middleware de escucha es una alternativa más simple y liviana a Redux-Saga, y elegirlo es sencillo. A menos que mantenga un código base que dependa en gran medida de Redux-Saga, vale la pena explorarlo. Si no cumple con su caso de uso, puede usar Redux-Saga u otro middleware con excelentes resultados.

Aunque el middleware de escucha es la funcionalidad que falta para que RTK ofrezca soluciones internas a la mayoría de los problemas que Redux-Saga parece resolver, no significa que cubra todos los casos de uso.

También vale la pena mencionar que este artículo hizo una comparación básica. Es posible que no sepamos el verdadero poder o las limitaciones del middleware de escucha hasta que se haya utilizado ampliamente en la producción, aunque muchos desarrolladores ya lo están utilizando de manera creativa para resolver problemas.

Si hay algo que me he perdido, házmelo saber en la sección de comentarios a continuación.

Fuente: https://blog.logrocket.com/redux-toolkits-new-listener-middleware-vs-redux-saga/

 #middleware #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

Ahebwe  Oscar

Ahebwe Oscar

1620192840

How Django Middleware Works?

How Django Middleware Works?

 April 25, 2021  Deepak@321  0 Comments

Welcome to my Blog, in this article we learn about How Django Middleware Works?

Django Middleware is a lightweight, low-level plugin system that modifies Django’s input and output. It is a framework that integrates Django for the processing of queries and answers. You can use middleware if you want to change the request object.

Django maintains a list of middleware for each project. Middleware allows you to edit requests from the browser before they reach Django, and to view the response from the view before they reach the browser. The middleware is applied in the same order as it is added to the list in the Django settings. If a new Django project has added a number of middlewares, in most cases they cannot be removed. Middleware is a checkmark that modifies the Django query and response objects.

In order for middleware to play a role, it is dependent on other middleware. For example, AuthenticationMiddleware stores the authenticated user session and executes the SessionMiddleware.

#django #django middleware #django middleware works #how django middleware works #structure of middleware in django

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