DEV Community

Cover image for Redux Rematch - State Management
kpiteng
kpiteng

Posted on

Redux Rematch - State Management

Rematch is an enhanced version of Redux with added few more features, clean architecture, less boilerplate for React Developers. Rematch uses the same redux concept and also has a persistor to persist store data.

Please download full source code from our GitHub.

Let’s look into architecture,

Provider -

<Provider store={store}>
Enter fullscreen mode Exit fullscreen mode

Provider contains an argument store. Here, we need to pass our store object in store argument to initialize Rematch Store.

See What's new in React Navigation 6.x.

Store -

To initialize a store we need to pass models, plugins, redux, etc. Let’s check the syntax,

export default init({
    models,
    plugins: [loadingPlugin, persistPlugin],
    redux: {
      middlewares: [logger],
    },
  })
Enter fullscreen mode Exit fullscreen mode

Models - Models contain state, reducer and effects in one instead of separate like Redux.

Plugins - Plugins contain Loading Plugins, Persist Plugins.

Redux - Contains middleware to perform some action in middleware between store & frontend.

See, How to improve performance of React Application?

Models -

Models are the core part of Store. Model contains state variables, reducers, effects. Frontend will dispatch action, which executes in effects and once effect computed task it will dispatch to reducer and reducer is responsible for updating state variables. So whenever the state variable is updated our component is also notified.

As you can see in Image, when UI Dispatch action it executes effects in model, once effect completed its execution it dispatches to reducers and reducer is only responsible to update state. Once the state gets updated it will re-render the UI Component.

export const TaskList = {
    state: {
      arrTasks: [],
      fetchError: undefined
    },
    reducers: {
      setTasks(state, payload) {
        return {
          ...state,
          arrTasks: payload,
        };
      },
      setError(state, payload) {
        return {
        ...state,
        fetchError: payload
        }
      }
    },
    effects: (dispatch) => ({
      async fetchTaskList() {
        try {
          const data = [
              { "taskID": 1, "taskName": "Task #1" },
              { "taskID": 2, "taskName": "Task #2" },
              { "taskID": 3, "taskName": "Task #3" }
          ]
          dispatch.TaskList.setTasks(data);
        } catch (error) {
          dispatch.TaskList.setError(error);
        }
      },
    }),
  };
Enter fullscreen mode Exit fullscreen mode

Plugins -

Rematch allows various plugins, you can set Loading Plugins, Persist Plugins.

Loading Plugins -

When we dispatch any action, we have to wait for a fraction of time until we receive results (API Call, Local Operation). Redux in-built having plugins that manage and return us loading = true which effect (action) being in-progress.

You can create Loading PlugIns using createLoadingPlugin, and pass your model name/actionname in the whitelist. As you can see we have taskList (model), fetchTaskList (action). So after this code, whenever the fetchTaskList operation is in-progress we will get loading as true, once it’s completed we get loading as false.

import createLoadingPlugin from '@rematch/loading';

export const loadingPlugin = createLoadingPlugin({
  whitelist: [taskList/fetchTaskList'],
});
Enter fullscreen mode Exit fullscreen mode

Discover Top 10 React Hooks Library

Persist Plugins -

Persist Plugins contains all required configuration like, whitelist, blacklist, version, storage, transforms, Let’s discuss each in details,

whitelist - Specify a list of models which you want to store in storage, even though you kill an application, restart application.

blacklist - Specify list of models which you don’t want to store in storage, so next time it will load with initial state, instead of saved state.

version - version of store,each to you deploy new application increase the version number, which help to migrate data from older version to newer version.

storage - Specify the storage when your store data is saved, for mobile applications ideally we prefer AsyncStorage, while in web we prefer default Web Storage.

transform - Specify Filters, many times you are required to blacklist a model (don’t want to store state value/ persist state value) but you want to store a few key/pairs only. So you can create whitelist filters for a few keys, and pass the model name in blacklist. So Rematch will only store key/pairs which you have sent in the whitelist.

Redux -

Redux contains middlewares, you can specify middlewares. You can create your own middleware to perform some action between UI Component and Effect which action dispatch by UI Component.

For example, let’s specify logger as middleware, so when you dispatch action, it will log in the console to track actions, same way whenever reducer updates the state at the same time logger also logs in console to update users about Rematch actions.

import logger from 'redux-logger';
redux: {
  middlewares: [logger],
}
Enter fullscreen mode Exit fullscreen mode

Now Let’s take an example of a Model and dispatch actions from the Component.

Model - TaskList.js

export const TaskList = {
    state: {
      arrTasks: [],
      fetchError: undefined
    },
    reducers: {
      setTasks(state, payload) {
        return {
          ...state,
          arrTasks: payload,
        };
      },
      setError(state, payload) {
        return {
        ...state,
        fetchError: payload
        }
      }
    },
    effects: (dispatch) => ({
      async fetchTaskList() {
        try {
          const data = [
              { "taskID": 1, "taskName": "Task #1" },
              { "taskID": 2, "taskName": "Task #2" },
              { "taskID": 3, "taskName": "Task #3" }
          ]
          dispatch.TaskList.setTasks(data);
        } catch (error) {
          dispatch.TaskList.setError(error);
        }
      },
    }),
  };
Enter fullscreen mode Exit fullscreen mode

Component - Tasks.js

import React, {useEffect} from 'react';
import {
    SafeAreaView, View, FlatList, Text
} from 'react-native';
import { connect } from 'react-redux';

function Tasks(props) {

    useEffect(() => {
      props.fetchTaskList();
    }, []);

    const TaskListItem = ({task}) => {
      return (
        <View style={{flex: 1, marginVertical: 10, marginHorizontal: 10}}>
          <Text>{task.taskName}</Text>
        </View>
      )
    }

    console.log(JSON.stringify(props.arrTasks));

    return (
      <SafeAreaView style={{flex: 1}}>
         <FlatList
            data={props.arrTasks}
            renderItem={({ item, index }) => <TaskListItem task={item}/>}
            keyExtractor={(item, index) => item.taskID}
        />
      </SafeAreaView>
    );
  }
  const mapStateToProps = ({ TaskList, loading }) => ({
    arrTasks: TaskList.arrTasks,
    loading: loading.effects.TaskList.arrTasks,
  });

  const mapDispatchToProps = ({ TaskList: { fetchTaskList } }) => ({
    fetchTaskList: () => fetchTaskList(),
  });

  export default connect(mapStateToProps, mapDispatchToProps)(Tasks);
Enter fullscreen mode Exit fullscreen mode

As you can see in Tasks Component, We are dispatching fetchTaskList on load of Component. Will call effect in TaskList.js model. Once fetch operation completed it will dispatch setTasks (action), which calls function in reducers, and reducer will update state variables of arryTasks, which in result re-render our Tasks Component.

Please download full source code from our GitHub.

Thanks for reading Blog!

KPITENG | DIGITAL TRANSFORMATION
www.kpiteng.com/blogs | hello@kpiteng.com
Connect | Follow Us On - Linkedin | Facebook | Instagram

Top comments (0)