Files
freeCodeCamp/guide/portuguese/redux/redux-middleware/index.md

9.0 KiB

title, localeTitle
title localeTitle
Redux Middleware Middleware Redux

Introdução

Neste guia irão ser apresentados alguns conceitos básicos de Middleware em Redux.

Se já possuir experiência com node.js e bibliotecas orientadas para servidor, tais como Express ou Koa, deverá já estar familiarizado com o conceito.

Em qualquer uma das bibliotecas mencionadas, o middleware não é nada mais nada menos que um bloco de código que reside entre os pedidos vindos do cliente, a biblioteca e a resposta enviada de volta.

Algumas das bibliotecas de middleware mais usadas tanto pelo Express ou Koa são por exemplo:

  • Helmet - Aplica um nível de segurança básico ás aplicações.
  • Winston - Efetua log de eventos que ocorrem na aplicação.
  • Compression - Comprime respostas HTTP.

Redux Middleware

Em Redux, o middleware funciona de forma similar, mas com o foco em situações diferentes.

Aqui o middleware é um bloco de código que interceta cada ação desencadeada, podendo chegar a modificar o seu conteúdo, ou cancelar por completo.

Isto sequer antes de chegar ao redutor.

Uma das bibliotecas mais populares a ser usada com Redux, é nada mais nada menos que a biblioteca Thunk.

Informação adicional acerca de thunks e como configurar poderá ser consultada aqui

Exemplo Simples

O bloco de código abaixo simula uma aplicação Redux simples e será usada como exemplo para demonstrar como o middleware poderá ser implementado.

 import {createStore} from  "redux";
   
 // redutor
 const reducer=(initialState=0,action)=>{
    if (action.type==="INC"){
        return initialState+1;
    }
     else if (action.type==="DEC"){
        return initialState-1;
    }
    return initialState;
 }
   
 // loja redux
 const store= createStore(reducer,1);
   
 // ações quer irão ser desencadeadas
 store.dispatch({type:"INC"});
 store.dispatch({type:"INC"});
 store.dispatch({type:"INC"});
 store.dispatch({type:"DEC"});
 store.dispatch({type:"DEC"});
 store.dispatch({type:"DEC"});

Com base no código á disposição, se fosse necessário implementar um middleware para lidar com o logging na aplicação, serão necessárias algumas alterações ao código.

 import {createStore, applyMiddleware} from  "redux";
   
 // redutor
 const reducer=(initialState=0,action)=>{
    if (action.type==="INC"){
        return  initialState+1;
    }
    else if (action.type==="DEC"){
        return  initialState-1;
    }
    return initialState;
 }
   
 // a implementação do middleware 
 const logger=(store)=>(next)=>(action)=>{
     console.log("ação disparada",action)
     /* move o fluxo da aplicação para o middleware seguinte, 
     ou caso não exista mais nenhum para o redutor */
    next(action);  
 }  
  const middleware=applyMiddleware(logger);
   
 //loja/store redux com o middleware definido
 const store= createStore(reducer,1,middleware);
 // ações a serem disparadas
 store.dispatch({type:"INC"});
 store.dispatch({type:"INC"});
 store.dispatch({type:"INC"});
 store.dispatch({type:"DEC"});
 store.dispatch({type:"DEC"});
 store.dispatch({type:"DEC"});

Quando o bloco de código terminar a sua execução, deverá existir o seguinte na consola do browser:

ação disparada Object {type: "INC"}
ação disparada Object {type: "INC"}
ação disparada Object {type: "INC"}
ação disparada Object {type: "DEC"}
ação disparada Object {type: "DEC"}
ação disparada Object {type: "DEC"}

Encadeamento de múltiplos middlewares

Em Redux, tal como por exemplo no Express, é possível encadear múltiplos middlewares que irão trabalhar em conjunto.

O bloco de código seguinte extende o exemplo básico usado anteriormente e irá adicionar um novo middleware para lidar com o tratamento de erros que poderiam ocorrer na aplicação.

  import {createStore, applyMiddleware} from  "redux";
   
  // o redutor
  const reducer=(initialState=0,action)=>{
    if (action.type==="INC"){
        return initialState+1;
    }
    else if (action.type==="DEC"){
        return initialState-1;
    }
    else if (action.type==="ERRO"){
        throw new Error("Erro na aplicação")
    }
    return initialState;
  }
   
 // middleware inicial definido
 const logger=(store)=>(next)=>(action)=>{
    console.log("ação disparada",action)
    /* move o fluxo da aplicação para o middleware seguinte, 
    ou caso não exista mais nenhum para o redutor */
    next(action) 
  };
  // o novo middleware responsável pelo tratamento de erros
const error=(store)=>(next)=>(action)=>{
    try{
        /* move o fluxo da aplicação para o middleware seguinte,
         ou caso não exista mais nenhum para o redutor */
      next(action);  // 
    }
    catch (e){
      console.log("Ocorreu o seguinte erro",e)
    }
 };
  // adiciona o middleware definido á aplicação
 const middleware=applyMiddleware(logger,error);
   
 // a loja/store redux com o middleware definido
 const store= createStore(reducer,1,middleware)  
 // ações a serem disparadas
 store.dispatch({type:"INC"});
 store.dispatch({type:"INC"});
 store.dispatch({type:"INC"});
 store.dispatch({type:"DEC"});
 store.dispatch({type:"DEC"});
 store.dispatch({type:"DEC"});
 store.dispatch({type:"ERRO"})

Quando terminar a sua execução, a consola no browser deverá conter o seguinte.

ação disparada Object {type: "INC"}
ação disparada Object {type: "INC"}
ação disparada Object {type: "INC"}
ação disparada Object {type: "DEC"}
ação disparada Object {type: "DEC"}
ação disparada Object {type: "DEC"}
Ocorreu o seguinte erro: Erro na aplicação

Criação de Middleware

Em Redux, o middleware não é nada mais nada menos que uma ou mais funções com uma estrutura idêntica á seguinte:

const reduxMiddleware = (store)=>(next)=>(action) => {
 // faz alguma coisa
}

Nota:

Ao olhar para o bloco de código acima, o que aparenta ser uma simples chamada a uma função e o valor retornado guardado numa constante. É na realidade é uma função que recebe como parâmetro a store Redux, irá retornar uma outra função, esta que por sua vez, tem como parâmetro o callback next e irá retornar uma outra função que tem como parâmetro action, esta sim irá executar o que se encontra definido num qualquer middleware que foi criado.

Isto poderá soar algo estranho.

Porquê usar esta abordagem em vez de utilizar três parâmetros?

Na verdade esta técnica é extremamente útil, vinda da programação funcional denominada de currying, o que permite inúmeras vantagens em termos de desenvolvimento aplicacional. A diferença significativa em usar esta abordagem neste caso, consiste na forma em como se irá invocar a função de middleware.

// invocação sem ser usado currying.
// NÃO é a forma que se irá invocar a função acima.
reduxMiddleware(store, next, action)
// vs invocar a versão que usa currying.
//A forma que se irá invocar a função acima.
reduxMiddleware(store)(next)(action)

Os parâmetros aqui são os seguintes:

1.) store - a store/loja Redux da aplicação, que ao invocar-se por exemplo o método getState(), retorna o presente estado em que a store/loja está e os seus valores.

let currentState = store.getState();

2.) next - callback que fará com que a aplicação continue o seu fluxo de execução para um novo middleware definido ou para o redutor, caso não existam mais middlewares definidos e adicionados.

next(action)

3.) action - a ação despoletada que irá atualizar o estado da store/loja.

Para terminar, vamos agora usar a informação á nossa disposição para criar um middleware que irá efetuar o log na consola do seguinte texto "Utilizador Atualizado!" cada vez que a ação com o tipo "ATUALIZA_UTILIZADOR" for desencadeada.

const updateUserLogger = (store)=>(next)=>(action) => {
 if (action.type === "ATUALIZA_UTILIZADOR") {
   console.log("Utilizador Atualizado!");
 }
 next(action);
};

Mais Informações: