0

As an example, I have the below code:

Child component (where I want to access the latest activeGenerators list)

function MainGameHandler() {
  const resourceCtx = useContext(ResourceContext);

  const [show, setShow] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => {
      if (resourceCtx.activeGenerators.length > 0) {
        console.log("more than zero");
      } else {
        console.log("hm");
      }
    }, 1000);
    return () => clearInterval(interval);
  }, []);

 

Component that handles the context:

const defaultGeneratorState = {
  activeGenerators: [],
};

function generatorReducer(state, action) {
  if (action.type === "ADD") {
    const updatedGenerators = state.activeGenerators.concat(action.generator);

    return {
      activeGenerators: updatedGenerators,
    };
  }
}

function ResourceProvider(props) {
  const [atomicVal, setAtomicVal] = useState(0);
  const [energyVal, setEnergyVal] = useState(0);

  const [generatorState, dispatchGeneratorAction] = useReducer(
    generatorReducer,
    defaultGeneratorState
  );

  function addActiveGenerator(generator) {
    dispatchGeneratorAction({ type: "ADD", generator: generator });
  }

  function updateAtomicVal(value) {
    setAtomicVal((prevState) => prevState + value);
  }

  function updateEnergyVal(value) {
    setEnergyVal((prevState) => prevState + value);
  }

  const resourceContext = {
    atomicVal: atomicVal,
    energyVal: energyVal,
    updateAtomicVal: updateAtomicVal,
    updateEnergyVal: updateEnergyVal,
    addGenerator: addActiveGenerator,
    activeGenerators: generatorState.activeGenerators,
  };
  return (
    <ResourceContext.Provider value={resourceContext}>
      {props.children}
    </ResourceContext.Provider>
  );
}

export default ResourceProvider;

In another child component, I am updating the value "activeGenerators" in my context. However, this isn't triggering a re-render of the child component "MainGameHandler". If I manually make a minor change (like to the log string) and force the component to re-render, then I can see the code works as expected. Why isn't my context update re-rendering the component on its own, my understanding as that it should?

I'm checking if it's working by viewing the log from the useEffect() call in the child component.

Edit: The component that adds new items to activeGenerators

import { ListGroup } from "react-bootstrap";
import React from "react";
import { useContext } from "react";
import ResourceContext from "../store/resource-context";

function EnergyGeneratorsList() {
  const resourceCtx = useContext(ResourceContext);
  const generators = [
    {
      id: 1,
      name: "generator 1",
      production: 1,
      cost: 1,
    },
    {
      id: 2,
      name: "generator 2",
      production: 2,
      cost: 2,
    },
  ];

  const generatorList = generators.map((gen) => (
    <ListGroup.Item
      variant="dark"
      action
      key={gen.id}
      onClick={onGeneratorClick}
    >
      {gen.name}
    </ListGroup.Item>
  ));

  function onGeneratorClick(event) {
    const selectedGenerator = generators.filter((obj) => {
      return obj.name === event.target.innerText;
    });

    resourceCtx.addGenerator(selectedGenerator);
  }

  return (
    <React.Fragment>
      <ListGroup>{generatorList}</ListGroup>
    </React.Fragment>
  );
}

export default EnergyGeneratorsList;
4
  • Please put the code or component that call addActiveGenerator Commented Dec 26, 2021 at 19:43
  • @HDM91 hi mate, just done an edit to show the additional component that calls addActiveGenerator. Commented Dec 26, 2021 at 19:48
  • The question is a bit unclear, a) where exactly do you expect to receive a state update (which particular variable is not being updated)? b) the code is still bloated, you should reduce the problem to minimum sample before asking people for help (remove unrelated components, code, links to styles etc) and ideally put a working copy to some online sandbox. But I've noticed one thing - ` EnergyGeneratorsList` doesn't use generators from context, it declares its own generators variable as a static array which will obviously never be updated. Commented Dec 26, 2021 at 19:58
  • Hi @amik I've done another edit to try and cut down the unrelated code. To clarify my problem, I click the <ListGroup> component in EnergyGeneratorsList which adds a new generator via the context method. I'm expecting this update to be reflected in MainGameWindow (where I'm trying to access the list via useEffect() ), but it keeps logging in the else block. If I update the component code, by say changing the log message, it starts logging correctly in the "if" block, showing it's now receiving the updated list. I don't know why it isn't automatically updated? Commented Dec 26, 2021 at 20:05

1 Answer 1

1

The useEffect function is executed with resourceCtx value from first render and won't receive any updates. This particular thing can be solved by passing it to a ref:

  const resourceCtx = useContext(ResourceContext);
  const resourceCtxRef = useRef();
  resourceCtxRef.current = resourceCtx;

  const [show, setShow] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => {
      // this should work as the ref is a mutable object that  receives updates
      if (resourceCtxRef.current.activeGenerators.length > 0) {
        console.log("more than zero");
      } else {
        console.log("hm");
      }
    }, 1000);
    return () => clearInterval(interval);
  }, []);

but it depends what exactly are you trying to do. I doubt that console.logging a value every second is what you will do at the end, so another solution may be more suitable to your particular case.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.