Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement observable watching for value in React Context

Let's say I'm having a Parent Component providing a Context which is a Store Object. For simplicity lets say this Store has a value and a function to update this value

class Store {
// value

// function updateValue() {}

}

const Parent = () => {
  const [rerender, setRerender] = useState(false);
  const ctx = new Store();

  return (
    <SomeContext.Provider value={ctx}>
      <Children1 />
      <Children2 />
      .... // and alot of component here
    </SomeContext.Provider>
  );
};

const Children1 = () => {
 const ctx = useContext(SomeContext);
 return (<div>{ctx.value}</div>)
}

const Children2 = () => {
 const ctx = useContext(SomeContext);
 const onClickBtn = () => {ctx.updateValue('update')}
 return (<button onClick={onClickBtn}>Update Value </button>)
}

So basically Children1 will display the value, and in Children2 component, there is a button to update the value.

So my problem right now is when Children2 updates the Store value, Children1 is not rerendered. to reflect the new value.

One solution on stack overflow is here. The idea is to create a state in Parent and use it to pass the context to childrens. This will help to rerender Children1 because Parent is rerendered. However, I dont want Parent to rerender because in Parent there is a lot of other components. I only want Children1 to rerender.

So is there any solution on how to solve this ? Should I use RxJS to do reative programming or should I change something in the code? Thanks

like image 882
Jake Lam Avatar asked Jun 22 '26 12:06

Jake Lam


1 Answers

You can use context like redux lib, like below

This easy to use and later if you want to move to redux you change only the store file and the entire state management thing will be moved to redux or any other lib.

Running example: https://stackblitz.com/edit/reactjs-usecontext-usereducer-state-management

Article: https://rsharma0011.medium.com/state-management-with-react-hooks-and-context-api-2968a5cf5c83

Reducers.js

import { combineReducers } from "./Store";

const countReducer = (state = { count: 0 }, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { ...state, count: state.count + 1 };
    case "DECREMENT":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

export default combineReducers({ countReducer });

Store.js

import React, { useReducer, createContext, useContext } from "react";

const initialState = {};
const Context = createContext(initialState);

const Provider = ({ children, reducers, ...rest }) => {
  const defaultState = reducers(undefined, initialState);
  if (defaultState === undefined) {
    throw new Error("reducer's should not return undefined");
  }
  const [state, dispatch] = useReducer(reducers, defaultState);
  return (
    <Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>
  );
};

const combineReducers = reducers => {
  const entries = Object.entries(reducers);
  return (state = {}, action) => {
    return entries.reduce((_state, [key, reducer]) => {
      _state[key] = reducer(state[key], action);
      return _state;
    }, {});
  };
};

const Connect = (mapStateToProps, mapDispatchToProps) => {
  return WrappedComponent => {
    return props => {
      const { state, dispatch } = useContext(Context);
      let localState = { ...state };
      if (mapStateToProps) {
        localState = mapStateToProps(state);
      }
      if (mapDispatchToProps) {
        localState = { ...localState, ...mapDispatchToProps(dispatch, state) };
      }
      return (
        <WrappedComponent
          {...props}
          {...localState}
          state={state}
          dispatch={dispatch}
        />
      );
    };
  };
};

export { Context, Provider, Connect, combineReducers };

App.js

import React from "react";
import ContextStateManagement from "./ContextStateManagement";
import CounterUseReducer from "./CounterUseReducer";
import reducers from "./Reducers";
import { Provider } from "./Store";

import "./style.css";

export default function App() {
  return (
    <Provider reducers={reducers}>
      <ContextStateManagement />
    </Provider>
  );
}

Component.js

import React from "react";
import { Connect } from "./Store";

const ContextStateManagement = props => {
  return (
    <>
      <h3>Global Context: {props.count} </h3>
      <button onClick={props.increment}>Global Increment</button>
      <br />
      <br />
      <button onClick={props.decrement}>Global Decrement</button>
    </>
  );
};

const mapStateToProps = ({ countReducer }) => {
  return {
    count: countReducer.count
  };
};

const mapDispatchToProps = dispatch => {
  return {
    increment: () => dispatch({ type: "INCREMENT" }),
    decrement: () => dispatch({ type: "DECREMENT" })
  };
};

export default Connect(mapStateToProps, mapDispatchToProps)(
  ContextStateManagement
);
like image 112
Rahul Sharma Avatar answered Jun 25 '26 02:06

Rahul Sharma