Getting deeper understanding of React-Redux by building it myself

From the past few days, I have been playing with react and I am loving the framework. React makes it painless for me to create interactive UIs. Designing simple views for each state in my application and the impressive thing is that React efficiently update and render just the right component when my data changes. The first few days I was in a trap because there were many ways to pass data into an application like using props to pass data from parent to child components and rendering the components, another way using react-redux in which we pass data with the store. The store will provide the data to the application, and the way which helps me in a deeper understanding of React-Redux using useReducer and useContext Hooks.

We often use the react-redux library in our React apps when we decide to include some sort of state management tool. The react library makes it very easy to use Redux in React. The library (react-redux) makes the use of the Context API to pass the store down to the nesting components without making the long props chaining.

React-Redux working


Redux works on three principles:


First Principle


"The state of your whole application is stored in an object tree within a single store."
Maintain our application state in a single object which would be managed by the Redux store.

Second Principle


"The only way to change the state is to emit an action, an object describing what happened"
To update the state of your app, you need to let Redux know about that with action.
Not allowed to directly update the state object.

Third Principle


"To specify how the store tree is transformed by actions, you write pure reducers."
Reducer={previousState,action}=> new State

So these three principles clearly help me in understanding the functionality of the React-Redux. So now I will explain to you with some pseudocode.

It uses the old React context to provide the store returned by the createStore in the redux library down the component tree.
const store = createStore{reducers}
class App {
    render() {
        return (
            <Provider store={store}>
                <MyComponent />
            </Provider>
        )
    }
}



It uses the connect function to create the higher-order components
class LoginComponent extends Component {
    // ...
}
export default connect(LoginComponent)


that connect the dispatch function in the store to the component and also maps the state of the store to the props of the component.
class LoginComponent extends Component {
    // ...
}
export default connect(LoginComponent)

With it, the components get the state and also dispatch actions to the store to change the state.

So , now after understanding the basic principle and some code. Let's deep dive into building our own react-redux using hooks. Let's first start with some basic of hooks and about hooks, we will be using in building react-redux.

Hooks

Hooks is one of the features ever added to the React library to provide scalability to application. Hooks brought the concept of 'state' to the functional component. Hooks will help you to create and manage local states on their own just like stateful class components into functional components. This is made possible by using the useState hook.

useReducer

useReducer is a hook used to set up and manage transactions in our components. So general form of useReducer is
const [state, dispatch] = useReducer((state, action)=> {...}, initialState)

useContext

This hook is used to get the current context of the provider. To create and provide the context, we use
React.createContext API
const myContext = React.createContext()


we put the root the component between mycontext provider
function App() {
    return (
        <myContext.Provider value={900}>
            <Root />
        </myContext.Provider>
    )
}

To consume the value provided by the <mycontext.provider></mycontext.provider> we use the useContext hook.
function Root() { const value = useContext(myContext) return ( <> <h3>My Context value: {value} </h3> </> )
}

So now after grabbing the basics we are going to use now bundle all these pieces of stuff to get our build API.

React-Redux - useReducer and useContext

Instead of using Redux to create our store, we use useReducer hook. Hook provides us with state and dispatch. The state will be passed down to the nesting components and the dispatch function that updates the state will also be passed down with the state, so any subscribing component can call the dispatch function to change the state, the change in state will be received from the top to the leaves
and all components subscribing to the store will be updated to reflect new changes.

useReducer will run on the topmost components where we want to store.so will create a context using React.createContext
import React, { Component, useReducer, useContext } from 'react';
import { render } from 'react-dom';const UserContext = React.createContext()

Now we will create a component routing render the component between UserContext.Provider component in App:

function Routing(props) {
    return (
        <div>
        </div>
    )
}function App () {
    return (
        <div>
            <UserContext.Provider>
                <Routing />
            </UserContext.Provider>        </div>    )
}render(<App />, document.getElementById('root'));


Now we will create our store using useReducer.We will make our state to hold the authenticated property. So we will create an initial state:
const initialState = {
    Authenticated: false
}

We will pass it to useReducer as the initial state, then we create a reducer function that will update the state based on the type of action dispatched.
function Routing(props) {
    return (
        <div>
        </div>
    )
}function App () {    const initialState={authenticated:false}    const [state, dispatch] = useReducer((state, action) => {
      switch(action.type) {
        case 'LOGIN':
          return { authenticated: action.payload }
        default:
          return state
      }
    }, initialState);    return (
        <div>
            <UserContext.Provider>
                <Routing />
            </UserContext.Provider>        </div>    )
}render(<App />, document.getElementById('root'));



See, we have our reducer function to listen for LOGIN action, we change the authenticated state with the action payload as value. If no action is caught we return the current state.

Now we will pass state and dispatch to UserContext.Provider as a prop:
function Routing(props) {
    return (
        <div>
        </div>
    )
}function App () {    const initialState={authenticated:false}    const [state, dispatch] = useReducer((state, action) => {
      switch(action.type) {
        case 'LOGIN':
          return { authenticated: action.payload }
        default:
          return state
      }
    }, initialState);    return (
        <div>
            <UserContext.Provider value={{state,dispatch}}>
                <Routing />
            </UserContext.Provider>        </div>    )
}render(<App />, document.getElementById('root'));



So Let's provide the functionality to Routing component first
function Routing(props) { return ( <Switch>
<Route exact path="/login">
<Login/>
</Route>
<Route exact path="/logout">
<Logout/>
</Route>
<Route exact path="/dashboard">
<Dashboard/>
</Route>
</Switch> ) }


So let us create the login component and change our state from the login result
function Login(props){
const login=()=>{
const result=checkUser()
if(result.status){
dispatch({type:"LOGIN",payload:{authenticated:true,user:result.user}})
}
else{
alert("Credentials invalid")
}
}
return(
<div>
<button onClick={login}>Login</button>
</div>)
}
function Routing(props) { return ( <Switch>
<Route exact path="/login">
<Login/>
</Route>
<Route exact path="/logout">
<Logout/>
</Route>
<Route exact path="/dashboard">
<Dashboard/>
</Route>
</Switch>
) }
function App () { const initialState={authenticated:false} const [state, dispatch] = useReducer((state, action) => { switch(action.type) { case 'LOGIN': return { authenticated:action.payload.authenticated,userDetails:action.payload.user } default: return state } }, initialState); return ( <div> <UserContext.Provider value={{state,dispatch}}> <Routing /> </UserContext.Provider> </div> ) }render(<App />, document.getElementById('root'));

So when we fired the event from the login button it checks the user from the server if the user is valid then it fires the dispatch function which takes an object as a parameter with the type of action to be performed and payload(data) with which state is to be updated.

Now, we are done. Running the app we will see authenticated first set to false as an initial state. Then when we click on the login button we dispatch the function and changed the state with an authenticated set to true as passing used details which can be later used by the state value of UserContext.Provider in an application. Similarly, we can change the state of LOGOUT action changing the authenticated state value to false and user to null after firing an event in the form of dispatch.

Conclusion
We have successfully created the react-redux library using hooks:useReducer and useContext. The useContext produced the store while the useReducer consumed the store and connected it to the functional components props vale. Any action and state update, performed by a component, are picked up by the subscribing components no matter their relationship with the producing component.

Keep Reading! Keep Writing!

Thanks !!!





Comments

Post a Comment

Popular posts from this blog

Design Process to make it better

Working together is a success ...