Search code examples
javascriptreactjsreact-context

React components not rendering from global state using context API


I am trying to get my child component to look for the global state. As of now, I can log out my global state, I can see that it has been updated, but my components are not updating with the new value. I am not getting any errors but also have had a hard time find a good solution. I have the full repo(very small with only 1 component) if it helps here: https://github.com/jaysnel/context-api-react

Is there any reason my ChangeColor.js file is not updating with the new state change?

UpdateColor.js

import React, { useReducer, useEffect } from 'react'
import { useAppContext } from '../public/context/context'

export default function UpdateColor() {
    let { myGlobaData } = useAppContext();
    const [state, dispatch] = useReducer(reducer, myGlobaData);


    function reducer(state, action) {
        let newState = state.slice();
        console.log(state);
        if(newState !== undefined) {
            switch(action.type) {
                case 'UPDATE_COLOR':
                    newState[0].color = 'green';
                    return newState;
                default:
                    throw new Error();
            }
        }
    }

    return (
        <div>
            <button onClick={() => dispatch({type: 'UPDATE_COLOR'})}>Update Color</button>
        </div>
    )
}

ChangeColor.js

import React from 'react'
import { useAppContext } from '../public/context/context'

export default function ChangeColor() {

    const { myGlobaData } = useAppContext();
    console.log("ChangeColor.js", myGlobaData)

    return (
        <div>
            <h2 style={{color: myGlobaData[0].color}}>I Change Colors Based On Global State.</h2>
        </div>
    )
}

context.js

import { createContext, useContext } from 'react';


const AppContext = createContext();

export function AppWrapper({ children }) {
    const state = {
      myGlobaData: [
            {
                color: 'red',
                text: 'new text new me'
            }
      ],
    }


    return (
      <AppContext.Provider value={state}>
        {children}
      </AppContext.Provider>
    );
  }
  
  export function useAppContext() {
    return useContext(AppContext);
  }
  

Solution

  • I guess, your issue where did you used the reducer. Component ChangeColor.js don't know what are you doing inside UpdateColor.js. The solution is if put the reducer into global context context.js and then you have to acsess your reducer globally. I did created and pushed to github working example with two different approaches to using an actions reducer. working example

    UpdateColor.js

    import { useAppContext  } from '../public/context/context'
    
    export default function UpdateColor() {
      const { dispatch } = useAppContext();
    
      const withPayload = () => {
         dispatch({
           type: 'UPDATE_COLOR_WITH_PAYLOAD',
           payload: {color: 'blue', text: 'new text from updateColor.js'}})
      }
      const intoReducer = () => {
         dispatch({type: 'UPDATE_COLOR_INTO_REDUCER'})
      }
    
      return (
        <div>
          <button onClick={withPayload}>Update Color with payload</button>
          <button onClick={intoReducer}>Update Color into reducer</button>
        </div>
      )
    }
    

    ChangeColor.js

    import { useAppContext } from '../public/context/context'
    
    export default function ChangeColor() {
      const { state } = useAppContext();
    
      return (
        <div>
          <h2 style={{color: state.color}}>I Change Colors Based On Global State.</h2>
          <p style={{textAlign: 'center'}}>{state.text}</p>
        </div>
      )   
    }
    

    context.js

    import { createContext, useContext, useReducer } from 'react';
    
    const AppContext = createContext();
    
    export function useAppContext() {
      return useContext(AppContext);
    }
    
    function reducer(state, action) {
      switch (action.type) {
        case 'UPDATE_COLOR_WITH_PAYLOAD':
          return action.payload;
        case 'UPDATE_COLOR_INTO_REDUCER':
          action.color = 'green';
          action.text = 'new text from reducer';
          return action;
       default:
         return state;
      }
    }
    
    export function AppWrapper({ children }) {
       const initialState = { color: 'red', text: 'new text new me' };
       const [state, dispatch] = useReducer(reducer, initialState);
       return (
         <AppContext.Provider value={{ state, dispatch }}>
          {children}
         </AppContext.Provider>
       );
    }