DEV Community

Cover image for Redux Best Practice 2021 - Redux Cool
React JS Fan
React JS Fan

Posted on • Updated on

Redux Best Practice 2021 - Redux Cool

Build redux logic, without getting nervous ❤️

Redux Cool is an awesome tiny package that allows you to easily and intuitively write redux logic. It is the collection of two separate libraries, one designed to create reducer functions and the other to create action objects: (Reducers Creator for creating reducers functions and Actions Creator for creating actions object)

Installation

npm install redux redux-cool
Enter fullscreen mode Exit fullscreen mode

Getting Started

Create a file named src/accountReducer.js

src/accountReducer.js

import {reducersCreator} from "redux-cool"

const initialState = {
    profile: {
        data: null
    }
}

const reducerTree = {

    PROFILE: {

        SET: (state, action) => {
            const [data] = action.args
            state.profile.data = data
        },

        UPDATE_EMAIL: (state, action) => {
            const [email] = action.args
            state.profile.data.email = email
        }
    },

    CLEAR: (state, action) => {
        state.profile.data = null
    }

}

const accountReducer = reducersCreator(
    "ACCOUNT",
    initialState,
    reducerTree,
)

export default accountReducer

Enter fullscreen mode Exit fullscreen mode

As you can see in the example above, we create an accountReducer by calling the reducersCreator function and passing it three arguments:

  • "ACCOUNT" : It's the name of the reducer, it can be any String
  • initialState : It's the initial state of the reducer, it can be any Object
  • reducerTree : It's an Object (can have any deep and nested structure) that intuitively and in readible ways, defines handler functions for reducer. Handler functions as an argument take state and action and update the state. It automatically uses the immer library to do immutable updates with normal mutative code, like state.profile.data.email = email. There is no need to manually do immutable updates and return the result. If you are not familiar with the immer library, please look at it, it is very important.

As a result, we get the accountReducer function, which can handle the following type of actions:

  • types: "PROFILE/SET" or "ACCOUNT/PROFILE/SET"
  • types: "PROFILE/UPDATE_EMAIL" or "ACCOUNT/PROFILE/UPDATE_EMAIL"
  • types: "CLEAR" or "ACCOUNT/CLEAR"

As you can see, each handler can work with two types of actions, one consisting of the path described in reducerTree, the second is the same as the first type plus the reducer name that should be added from the beginning like "CLEAR" and "ACCOUNT/CLEAR". That is the most important and useful feature of this library.

In both cases ("CLEAR" and "ACCOUNT/CLEAR"), the CLEAR handler is called in the accountReducer, but when we have multiple reducers that have the CLEAR handler and we need to clear the state of all reducers, we must use "CLEAR" action type, but if we need to delete only the ACCOUNT reducer state we must use the "ACCOUNT/CLEAR" action type.

Now that we have the accountReducer, let's create the redux store

Create a file named src/store.js

src/store.js

import {createStore} from "redux"
import {actionsCreator} from "redux-cool"
import accountReducer from "./accountReducer.js"

// Create Store
const store = createStore(accountReducer)

// Dispatch Set Profile Action
store.dispatch(actionsCreator.PROFILE.SET({
        email: 'test@test',
        name: 'Test'
    })
)
console.log(store.getState())
// {
//     profile: {
//         data: {email: 'test@test', name: 'Test'}
//     }
// }


// Dispatch Update Email Action
store.dispatch(actionsCreator.PROFILE.UPDATE_EMAIL('test2@test2'))
console.log(store.getState())
// {
//     profile: {
//         data: {email: 'test2@test2', name: 'Test'}
//     }
// }



// Dispatch Clear Email Action
store.dispatch(actionsCreator.CLEAR())
console.log(store.getState())
// {
//     profile: {
//         data: null
//     }
// }

Enter fullscreen mode Exit fullscreen mode

Resources

Top comments (5)

Collapse
 
markerikson profile image
Info Comment hidden by post author - thread only accessible via permalink
Mark Erikson • Edited

Hi, I'm the primary Redux maintainer. We already have an official Redux Toolkit package that does all of this same functionality already (generating action types/action creators, reducers that use Immer, etc), as well as additional features. It's also written in TypeScript and designed for a good TS usage experience. You should really be using Redux Toolkit instead:

import { createSlice } from '@reduxjs/toolkit'

const initialState = []

const todosSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    todoAdded(state, action) {
      // ✅ This "mutating" code is okay inside of createSlice!
      state.push(action.payload)
    },
    todoToggled(state, action) {
      const todo = state.find(todo => todo.id === action.payload)
      todo.completed = !todo.completed
    },
})

export const {todoAdded, todoToggled} = todosSlice.actions
export default todosSlice.reducer
Enter fullscreen mode Exit fullscreen mode

See redux.js.org/tutorials/fundamental... for some examples of migrating existing Redux logic to RTK, and redux.js.org/tutorials/essentials/... for a larger tutorial that demonstrates building a "real-world" app with RTK.

Collapse
 
reactjsfan profile image
React JS Fan

@mark
It's not the same redux-toolkit, there is not even a feature similar to redux-toolkit ․ I will explain now․

1. Nested reducers tree capability

In redux-cool we can build our reducerTree with any deep and nested structure ․ which redux-toolkit cannot do. For example

const reducerTree = {

    PROFILE: {

        SET: (state, action) => {
            const [data] = action.args
            state.profile.data = data
        },

        UPDATE_EMAIL: (state, action) => {
            const [email] = action.args
            state.profile.data.email = email
        }
    },

    CLEAR: (state, action) => {
        state.profile.data = null
    }

}
Enter fullscreen mode Exit fullscreen mode

2. Ability to create actions independent of the reducer

For example:

import {actionsCreator} from "redux-cool"

const first_action = actionsCreator.MY.FIRST.ACTION("arg1", "arg2")
console.log(first_action)
//      {
//          type: "MY/FIRST/ACTION",
//          args: ["arg1", "arg2"],
//          cb: f() identity,
//          _index: 1
//      } 


const second_action = actionsCreator.This.is.my.second.action(2021)
console.log(second_action)
//      {
//          type: "This/is/my/second/action",
//          args: [2021],
//          cb: f() identity,
//          _index: 2
//      } 
Enter fullscreen mode Exit fullscreen mode

3. Actions With Callback capability

For example:

import {actionsCreator} from "redux-cool"


const my_callback = () => {
    console.log("Hello, I am callback!!!")
}

const callbackable_action = actionsCreator.CALLBACKABLE.EXAMPLE(1, 2, 3, my_callback)

console.log(callbackable_action)
//      {
//          type: "CALLBACKABLE/EXAMPLE",
//          args: [1, 2, 3],
//          cb: f() my_callback,
//          _index: 1
//      }

callbackable_action.cb()
//      "Hello, I am callback!!!"
Enter fullscreen mode Exit fullscreen mode

4. See: here

Collapse
 
wkrueger profile image
wkrueger

WTF happened here, someone created a dev.to profile impersonating React, then posted THIS.

Community work been hard these days.

Collapse
 
markerikson profile image
Info Comment hidden by post author - thread only accessible via permalink
Mark Erikson

Yipes, no kidding. I don't think they had that username when they posted the story, so maybe they renamed their account? Flagged and reported for spam.

Collapse
 
reactjsfan profile image
React JS Fan

It's not the same redux-toolkit, there is not even a feature similar to redux-toolkit ․ I will explain now․

1. Nested reducers tree capability

In redux-cool we can build our reducerTree with any deep and nested structure ․ which redux-toolkit cannot do. For example

const reducerTree = {

    PROFILE: {

        SET: (state, action) => {
            const [data] = action.args
            state.profile.data = data
        },

        UPDATE_EMAIL: (state, action) => {
            const [email] = action.args
            state.profile.data.email = email
        }
    },

    CLEAR: (state, action) => {
        state.profile.data = null
    }

}
Enter fullscreen mode Exit fullscreen mode

2. Ability to create actions independent of the reducer

For example:

import {actionsCreator} from "redux-cool"

const first_action = actionsCreator.MY.FIRST.ACTION("arg1", "arg2")
console.log(first_action)
//      {
//          type: "MY/FIRST/ACTION",
//          args: ["arg1", "arg2"],
//          cb: f() identity,
//          _index: 1
//      } 


const second_action = actionsCreator.This.is.my.second.action(2021)
console.log(second_action)
//      {
//          type: "This/is/my/second/action",
//          args: [2021],
//          cb: f() identity,
//          _index: 2
//      } 
Enter fullscreen mode Exit fullscreen mode

3. Actions With Callback capability

For example:

import {actionsCreator} from "redux-cool"


const my_callback = () => {
    console.log("Hello, I am callback!!!")
}

const callbackable_action = actionsCreator.CALLBACKABLE.EXAMPLE(1, 2, 3, my_callback)

console.log(callbackable_action)
//      {
//          type: "CALLBACKABLE/EXAMPLE",
//          args: [1, 2, 3],
//          cb: f() my_callback,
//          _index: 1
//      }

callbackable_action.cb()
//      "Hello, I am callback!!!"
Enter fullscreen mode Exit fullscreen mode

4. See: here

Some comments have been hidden by the post's author - find out more