React redux

A redux egy adatokat tároló objektum, amibe helyezhetünk adatokat és olvashatjuk ki őket bármelyik másik komponensben is. Reduxban célszerű tárolni a profil adatokat beállításokat, olyan adatokat amik ritkán változnak, de sokszor van rájuk szükség, így nem kell folyamatosan újra lekérdezni őket.

Tegyük fel a react-redux csomagot az npm-el.

A chrome böngészőben levő redux-os devtool-os plugin-t használhatjuk ha szükséges, hogy megtudd nézni a böngészőben a store/state aktuális tartalmát. Ez a fejlesztésnél nagy segítség lehet.
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd

A redux használatához szükséges fájlokat négy részre osztjuk. Actions, action-types, reducers, services.

Az action-type-ban definiáljuk a redux-ban használható hívások azonosítóit.
Példa action-type -ra:
 
// CITY
export const GET_CITY = 'GET_CITY';
export const GET_CITY_SUCCESS = 'GET_CITY_SUCCESS';
export const GET_CITY_FAILED = 'GET_CITY_FAILED';

export const GET_CITY_LIST = 'GET_CITY_LIST';
export const GET_CITY_LIST_SUCCESS = 'GET_CITY_LIST_SUCCESS';
export const GET_CITY_LIST_FAILED = 'GET_CITY_LIST_FAILED';

export const CREATE_CITY = 'CREATE_CITY';
export const EDIT_CITY = 'EDIT_CITY';
export const DELETE_CITY = 'DELETE_CITY';

Az action-ökben deklaráljuk azokat a függvényeket, amiket meghívunk majd a komponensekből. A service-eket hívjuk meg bennük és átadjuk a redux-nak a service fügvények értékeit.
Egy példa actions-re:
 
import * as CityService from '../services/city';
import * as actionTypes from 'modules/his/actions/action-types';

export const getCityList = (params) => {
  return async (dispatch) => {
    try {
      dispatch({
        type: actionTypes.GET_CITY_LIST,
      });

      let citylist = await CityService.getCityList(params);

      dispatch({
        type: actionTypes.GET_CITY_LIST_SUCCESS,
        payload: citylist,
      });

      return citylist;
    } catch (error) {
      console.log(error);
      dispatch({
        type: actionTypes.GET_CITY_LIST_FAILED,
        payload: error,
      });

      throw error;
    }
  };
};

export const getCity = (id) => {
  return async (dispatch) => {
    try {
      dispatch({ type: actionTypes.GET_CITY });
      const city = await CityService.getCity(id);
      dispatch({
        type: actionTypes.GET_CITY_SUCCESS,
        payload: city,
      });

      return city;
    } catch (error) {
      dispatch({ type: actionTypes.GET_CITY_FAILED });
      throw error;
    }
  };
};

export const createCity = (cityData) => {
  return async (dispatch, getState) => {
    let city = await CityService.createCity(cityData);

    if (city['City'].ID) {
      city = { ...cityData, ID: city['City'].ID };
    }
    dispatch({
      type: actionTypes.CREATE_CITY,
      payload: city,
    });

    return city;
  };
};

export const editCity = (id, cityData) => {
  return async (dispatch, getState) => {
    await CityService.editCity(cityData, id);

    dispatch({
      type: actionTypes.EDIT_CITY,
      payload: cityData,
    });

    return cityData;
  };
};

export const deleteCity = (id) => {
  return async (dispatch, getState) => {
    await CityService.deleteCity(id);

    dispatch({
      type: actionTypes.DELETE_CITY,
      payload: id,
    });

    return id;
  };
};

A reducer-ben kezeljük a fent definiált action-öket az action-type-okkal. Bekerülnek a redux state-be az adatok vagy onnan olvassuk ki őket és adjuk vissza az action-öknek.
Reducer példa fájl tartalma:
 
import {
  GET_CITY_LIST,
  GET_CITY,
  GET_CITY_FAILED,
  GET_CITY_LIST_SUCCESS,
  GET_CITY_SUCCESS,
  CREATE_CITY,
  DELETE_CITY,
  EDIT_CITY,
} from 'modules/his/actions/action-types';

const INITIAL_STATE = {
  loading: false,
  error: null,
  data: { data: [], count: 0 },
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case GET_CITY_LIST:
      return {
        ...state,
        loading: true,
      };
    case GET_CITY:
      return {
        ...state,
        loading: true,
      };
    case GET_CITY_SUCCESS:
      return {
        ...state,
        loading: false,
        city: action.payload || {},
      };
    case GET_CITY_LIST_SUCCESS:
      return {
        ...state,
        loading: false,
        data: { data: action.payload.data, count: action.payload.total },
      };
    case GET_CITY_FAILED:
      return {
        ...state,
        loading: false,
        error: action.payload,
      };
    case CREATE_CITY:
      return {
        ...state,
        data: { data: [...state.data.data, action.payload], count: state.data.count++ },
      };
    case EDIT_CITY:
      return {
        ...state,
        data: {
          data: [
            ...state.data.data.filter((city) => city.KOD !== action.payload.KOD),
            Object.assign({}, action.payload),
          ],
          count: state.data.count,
        },
      };
    case DELETE_CITY:
      return {
        ...state,
        data: {
          data: state.data.data.filter((city) => city.KOD !== action.payload),
          count: state.data.count--,
        },
      };
    default:
      return state;
  }
};

Service példa fájl, amit az action hív meg:
 
    import qs from 'query-string';
     
    import request from 'libs/request';
    import { Methods } from 'libs/http';
     
    const BASE_URL = '/city';
     
    export const getCity = async (id) => {
      return await request({
        method: Methods.GET,
        resource: `${BASE_URL}/show/${id}`,
      });
    };
     
    export const createCity = async (data) => {
      return await request({
        method: Methods.POST,
        resource: `${BASE_URL}/store`,
     
        data,
      });
    };
     
    export const editCity = async (data, id) => {
      return await request({
        method: Methods.PUT,
        resource: `${BASE_URL}/update/${id}`,
     
        data,
      });
    };
     
    export const deleteCity = async (id) => {
      return await request({
        method: Methods.DELETE,
        resource: `${BASE_URL}/destroy/${id}`,
      });
    };
     
    export const getCityList = async (params) => {
      return await request({
        method: Methods.GET,
        resource: `${BASE_URL}?${qs.stringify(params, {encode: false, arrayFormat: 'bracket'})}`,
      });
    };

Komponens-ben bekell "connect"-elnünk a használathoz:
 
import React, { PureComponent } from 'react';
import { connect } from 'react-redux'
import { getCityList, getCity, createCity, editCity, deleteCity } from './action/cityAction'

class CityPage extends PureComponent {
  static propTypes = {
    getCityList: PropTypes.func.isRequired,
    getCity: PropTypes.func.isRequired,
    createCity: PropTypes.func.isRequired,
    editCity: PropTypes.func.isRequired,
    deleteCity: PropTypes.func.isRequired,
  };

  ...
  load = async (params = this.props.params) => {
       const city = await this.props.getCityList(...params);
  }
}

const mapStateToProps = (state) => {
  return {
    city: state.city.data
  }
}

const mapDispatchToProps = { 
   getCityList, 
   getCity, 
   createCity, 
   editCity, 
   deleteCity 
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

Fejlesztéshez a redux tartalmát megtekinteni fel lehet tenni egy chrome extension-t, ami sokat segít.
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd

Olvasnivaló
https://react-redux.js.org/
https://react-redux.js.org/introduction/quick-start
2020.05.09.