DEV Community

hiro@
hiro@

Posted on

similar redux architecture example powered by react hooks API

I guess you guys have known hooks API already, haven't you? This article is one of the examples for React hooks API. First of all, here is the example code which I'm gonna describe it.
https://github.com/takahiro-saeki/react-example-code/tree/master/chapter2

and here is an example's screenshot.
screenshot of example

Let's get started!

Redux architecture with hooks API without Redux.

architecture structure.

./chapter2
├── index.html
├── package.json
└── src
    ├── Context.jsx
    ├── Provider.jsx
    ├── actions
    │   └── index.js
    ├── components
    │   ├── Header
    │   │   ├── index.jsx
    │   │   └── style.js
    │   ├── List
    │   │   ├── index.jsx
    │   │   └── style.js
    │   ├── ListArea
    │   │   ├── index.jsx
    │   │   └── style.js
    │   └── index.js
    ├── connect.js
    ├── containers
    │   └── App
    │       └── index.js
    ├── index.js
    ├── modules
    │   ├── fetchLogic.js
    │   ├── getFavoItems.js
    │   ├── getSearch.js
    │   └── useWindowWidth.js
    ├── pages
    │   └── MainView
    │       └── index.jsx
    ├── reducers
    │   ├── combineReducers.js
    │   ├── favorite.js
    │   ├── index.js
    │   └── posts.js
    └── store.js

It seems like similar to redux architecture. But there are no redux dependencies in this example.
and main logic is in index.js where is a root.

import React from 'react';
import { render } from 'react-dom';
import reducers from './reducers';
import Provider from './Provider';
import App from './containers/App';

document.body.style.margin = 0;

const Main = () => (
  <Provider reducer={reducers}>
    <App />
  </Provider>
);

render(<Main />, document.querySelector('#app'));

This Provider component has a reducer props, which passes to Context API.
take a look at Provider 's logic.

import React, { useState, useReducer, useEffect } from 'react';
import Context from './Context';

type Props = {
  children: any,
  reducer: () => {}
};

const Provider = ({ children, reducer }: Props) => {
  const [store, dispatch] = useReducer(reducer);
  const [state, setState] = useState({ isLoaded: false });

  useEffect(() => {
    dispatch({ type: '@init' });
    setState({ isLoaded: true });
  }, []);

  return (
    <Context.Provider value={{ dispatch, store }}>
      {state.isLoaded ? children : false}
    </Context.Provider>
  );
};

export default Provider;

Provider component has 2 props. 1st one is children , second one is reducer .
Inside of useEffect 's dispatch is, it's necessary for making store from reducer.
Same as redux's reducer, it is mandatry to set default state params in each reducer otherwise it will occur unexpected error.
And inside of combineReducer.js , it's combined each reducer which is being set as argument in combineReducer function.
Take a look at inside of combineReducer.js .

const combineReducers = reducer => {
  return (state = {}, action) => {
    const keys = Object.keys(reducer);
    const nextReducers = {};
    for (let i = 0; i < keys.length; i++) {
      const invoke = reducer[keys[i]](state[keys[i]], action);
      nextReducers[keys[i]] = invoke;
    }
    return nextReducers;
  };
};

export default combineReducers;

I mimicked redux combineReducer. I didn't know that reducer will be invoked every single time when action is dispatched.

connect logic

connect logic is really simple Higher Order Component.

import React, { useContext } from 'react';
import Context from './Context';

const connect = (mapState, mapDispatch) => {
  return WrappedComponent => {
    return () => {
      const { store, dispatch } = useContext(Context);
      return (
        <WrappedComponent {...mapState(store)} {...mapDispatch(dispatch)} />
      );
    };
  };
};

export default connect;

to use Context API as Hooks is so simple to use it. share store to mapState, share dispatch to mapDispatch, then you can use mapStateToProps and mapDispatchToProps like redux in presentational component everywhere if you connect it.

const App = ({ posts, addTodo, addFavoItem, favorite }) => {
  /* ...logics */
  return <MainView {...hundlers} />;
};

const mapStateToProps = store => ({
  posts: store.posts.data,
  favorite: store.favorite.favorite_posts
});

const mapDispathToProps = dispatch => ({
  addTodo: param => dispatch(addTodo(param)),
  addFavoItem: param => dispatch(addFavoItem(param))
});

export default connect(
  mapStateToProps,
  mapDispathToProps
)(App);

Seems like redux, right?

In conclusion.

In my humble opinion, I think it's really difficult to replace redux for using hooks api like redux. but it's possible and you can make similar logic like redux.
but if you are using redux middleware like redux-saga, you might have to think how to optimize those middleware.
And I wanna try to replace redux from example app which is using redux-saga or thunk or whatever famous library which people are heavily using in redux environment.

Another thing is that I'm feeling important, to use hooks api is improving DX for you definitely. So far I would love that. I might write something tutorial about hooks which is more porite and neat than this article. haha

Thanks and if you have a question, feel free to reach out to me. Any question is ok.
Bye!!

Top comments (1)

Collapse
 
droidmakk profile image
Afroze Kabeer Khan. M

What do you mean by DX?