React Redux binding with React Hooks and Proxy

React Redux binding with React Hooks and Proxy

React Redux binding with React Hooks and Proxy. Contribute to dai-shi/reactive-react-redux development by creating an account on GitHub. ... hooks without selectors · Benchmark alpha-released hooks API in React Redux with alternatives

Ever since I first learned Redux, I’ve been thinking about alternative ways to integrate it with React. My intuition was that Redux is super simple (which I like a lot), whereas react-redux’s performance optimizations make it complex. And while I’d still recommend that you use the official react-redux library for business products, if you’re playing with a toy app or you’re just starting to learn Redux, then you might want something more straightforward.

The React team recently introduced the new Hooks API, allowing for the use of stateful logic in function components. In particular, this allows for custom hooks — reusable chunks of state management logic. As a result, there have been many discussions about how to create hooks-based React bindings for Redux, and many proposals that would replace Redux completely, including mine.

But today, I’ll discuss something simpler: instead of replacing Redux, I’ll demonstrate how to develop custom hooks for Redux in a straightforward way. I’ll first describe a naive implementation, and later introduce a Proxy-based approach that seamlessly improves performance.

A naive implementation

Let’s start with a naive implementation. If you are not familiar with the Context and Hooks APIs, please visit the official docs to learn them first. The rest of this article assumes a basic understanding of them.

First, let’s create a single context that can pass around a Redux store.

const ReduxStoreContext = React.createContext();

const ReduxProvider = ({ store, children }) => (
  <ReduxStoreContext.Provider value={store}>
    {children}
  </ReduxStoreContext.Provider>
);

We then define two hooks: useReduxDispatch() and useReduxState(). The reason we have two separate hooks is that not all components will use both hooks at the same time, and we want to hide the usage of context within the implementation.

Incidentally, the implementation of useReduxDispatch() is very simple.

const useReduxDispatch = () => {
  const store = useContext(ReduxStoreContext);
  return store.dispatch;
};

The naive implementation of useReduxState() is a little more complex, and uses four hooks: useContext()useRef()useEffect() and useForceUpdate().

const useReduxState = () => { 
  const forceUpdate = useForceUpdate();
  const store = useContext(ReduxStoreContext);
  const state = useRef(store.getState());
  useEffect(() => {
    const callback = () => {
      state.current = store.getState();
      forceUpdate();
    };
    const unsubscribe = store.subscribe(callback);
    return unsubscribe;
  }, []);
  return state.current;
};

Basically, we just subscribe to any changes in the Redux store’s state. (A minor note: this doesn’t support changing the store on the fly, which may be important for testing.)

We can then implement useForceUpdate() as below.

const forcedReducer = state => !state;
const useForceUpdate = () => useReducer(forcedReducer, false)[1];

A usage example

If you are familiar with Redux and react-redux, then you’ll probably notice some issues with the above example.

To start with, if you click the +1* button, not only does it re-render the Counter component — it also* re-renders the TextBox component. Well, this is not a problem until it is a problem. For small apps, it works just fine. But for larger apps, it may cause problems with performance.

Really, we’d like to avoid unnecessary rendering if possible. If you were using react-redux, you might supply connect() with a [mapStateToProps](https://react-redux.js.org/api/connect#mapstatetoprops-state-ownprops-object "mapStateToProps") function to achieve this:

mapStateToProps: (state, ownProps?) => Object

mapStateToProps allows you to specify which part of the state will be used by a component. But actually defining it is a lot of extra work, and can be troublesome in some cases. It’s easy to add heavy computations in such functions, and I’ve found that beginners have trouble optimizing this due to lack of familiarity with memoization.

But what if you don’t need to specify a mapStateToProps function? What if you can use Proxy instead?

Improving useReduxState() with Proxy

JavaScript’s new Proxy object makes it possible to automatically detect which part of the state is used during rendering. Then when your component is notified of a new state, it will know if the relevant part has changed, and can re-render only when necessary.

Let’s modify our useReduxState() hook to implement this feature. For now, we’ll only worry about the first level of the state object (and we’ll ignore state that is deep in the object tree).

const useReduxState = () => { 
  const forceUpdate = useForceUpdate();
  const store = useContext(ReduxStoreContext);
  const state = useRef(store.getState());
  const used = useRef({});
  const handler = useMemo(() => ({
    get: (target, name) => {
      used.current[name] = true;
      return target[name];
    },
  }), []);
  useEffect(() => {
    const callback = () => {
      const nextState = store.getState();
      const changed = Object.keys(used.current)
        .find(key => state.current[key] !== nextState[key]);
      if (changed) {
        state.current = nextState;
        forceUpdate();
      }
    };
    const unsubscribe = store.subscribe(callback);
    return unsubscribe;
  }, []);
  return new Proxy(state.current, handler);
};

Now, the TextBox will not be re-rendered when you click +1, and vice versa.

This implementation is somewhat limited, and you might think that the shallow comparison is not enough. Indeed, if you already have a concrete design for your state structure, then that may be the case. However, if you start designing a new state structure, you could design it with first-level separation in mind. Also, note that for complex structures where you would need reselect with react-redux, you could use useMemo() in a function component so that it returns a memoized element.

The library

As it happens, I’ve developed a library called react-hooks-easy-redux that takes this approach. This library actually allows deep comparison thanks to proxyequal. (Still, the shallow comparison described in the previous section may work better in some use cases.)

View react-hooks-easy-redux at GitHub »

The repository contains several examples — here’s one that you can play with as a live Demoboard:

Final notes

There could be some edge cases where this approach or the library doesn’t work well. I would love to hear your feedback either by Twitter, or GitHub issues.

The official react-redux also has a discussion about the Proxy approach. The possibility remains open that they might implement it in the future. Would be nice.

Further reading:

Understanding React [setState](https://morioh.com/p/1250c804cabb/understanding-react-setstate "setState")

The Guide to Learning React Hooks (Examples & Tutorials)

How to add Dark Mode to a React App

Switching off the lights - Adding dark mode to your React app

Building Micro Frontends with React, Vue, and Single-spa

Creating Micro Frontends using Web Components (with Angular and React)

reactjs javascript

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

The essential JavaScript concepts that you should understand

The essential JavaScript concepts that you should understand - For successful developing and to pass a work interview

What is ECMAScript and How is it Different From JavaScript?

Many times developers use JavaScript and ECMAScript synonymously for each other. Though they are very closely linked to each other, it does not mean they are the same thing.here is a complete story on the history of JavaScript and how it came into existence. To cut the long story short, ECMA in ECMAScript refers to Europen Computer Manufacturers Association to which JavaScript 1.1 was submitted for standardization back in the year 1997.

Data Types In JavaScript

JavaScript data types are kept easy. While JavaScript data types are mostly similar to other programming languages; some of its data types can be unique. Here, we’ll outline the data types of JavaScript.

Forms of Composition in JavaScript and React

One of the core ideas in functional programming is composition: building larger things from smaller things. The canonical example of this idea should be familiar with legos.

JavaScript Memory Management System

The main goal of this article is help to readers to understand that how memory management system performs in JavaScript. I will use a shorthand such as GC which means Garbage Collection. When the browsers use Javascript, they need any memory location to store objects, functions, and all other things. Let’s deep in dive that how things going to work in GC.