DEV Community

sojin antony
sojin antony

Posted on • Originally published at Medium on

No more Redux (or Context) to manage complex data in your react applications, try…

No more Redux (or Context) to manage complex data in your react applications, try “useSyncExternalStore” instead

Simplifying State Management in React with useSyncExternalStore

As we all know, managing complex data in React applications can be a challenge. Context just resolving props drilling it will still re render all its children when a minor change happened in state.

Redux is the popular solution, but it can be overkill for simple use cases. And huge bundle size!. With React 18, we have a new hook called useSyncExternalStore that can help us create a global state management system without the need for Redux or Context.

Creating a Custom Store and Reducer

Let’s create a simple store and reducer to manage an array of editable items. We’ll define a Store interface with getState, dispatch, and subscribe methods. We’ll also create a ListReducer interface to represent the state of our array.

// store.ts
import { useCallback } from "react";
import { useSyncExternalStore } from "react";
import { DispatcherActions, initialState, ListReducer, StoreAction } from "./reducer";

export interface Store {
  getState: () => ListReducer;
  dispatch: (fn: DispatcherActions[string], action?: StoreAction) => void;
  subscribe: (onStoreChange: () => void) => () => void;
}

const createStore = (): Store => {
  let state = initialState;
  const getState = (): ListReducer => state;
  const listeners: Set<() => void> = new Set();
  const dispatch = (fn: DispatcherActions[string], action?: StoreAction) => {
    state = fn(state, action || { payload: undefined });
    listeners.forEach((l) => l());
  };
  const subscribe = (listener: () => void) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
  };
  return { getState, dispatch, subscribe };
};

//State created here.
export const store: Store = createStore();
Enter fullscreen mode Exit fullscreen mode

Implementing useAppSelector

Next, we’ll create a custom useAppSelector hook that uses useSyncExternalStore to select a specific part of the state.

// store.ts
export const useAppSelector = (store: Store, selector: (state: ListReducer) => string) =>
  useSyncExternalStore(
    store.subscribe,
    useCallback(() => selector(store.getState()), [store, selector]),
  );

// reducer.ts

export interface ListReducer {
  data: string[];
}
export const initialState: ListReducer = {
  data: []
};

export interface StoreAction {
  payload: any;
  type?: string;
}

export interface DispatcherActions {
  [key: string]: (state: ListReducer, action: StoreAction) => ListReducer;
}

const actions: DispatcherActions = {
  changeData(state, action) {
    state.data[action.payload.i] = action.payload.value;
    return state;
  },
};

export const {
  changeData,
} = actions;
Enter fullscreen mode Exit fullscreen mode

Now create react components

import Cell from "./cell";
import { store, useAppSelector } from "./store";

function App() {
  const items = useAppSelector(store, (state) => state.data);
  return (
    <>
      {items.map((d: string, i: number) => (
        <Cell i={i} />
      ))}
    </>
  );
}

export default App;

import { changeData } from "./store/reducer";
import { store, useAppSelector } from "./store";

const Cell = ({ i }: { i: number }) => {
  //Read only the expected value
  const val = useAppSelector(store, (state) => state.data[i]);
  const onChange = (e: { target: { value: any } }) => {
    store.dispatch(changeData, { payload: { val: e.target.value, i: i } });
  };
  console.log("Print This one each render Cell index:", i);
  return <input value={val} onChange={onChange} />;
};
export default Cell;
Enter fullscreen mode Exit fullscreen mode

Try this and type something in your text box and check console 😃. only one cell should be getting re rendered.

https://medium.com/media/1e7a4e7820c778caa2d3be31d72cc1c4/href

Rendering a Large Number of Input Boxes

This solution can even handle rendering a large number of input boxes without performance issues.

React Excel sheet

Yup it is working with zero issues. I have used same solution in my excel package,

react-spread-sheet-excel

More information of package can be found here

What If you need to render a lot of input boxes in screen?.

Hope this is help full.

Thanks

Sojin Antony

Top comments (0)