Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How do I get a component to "listen" to changes in global state in a sibling component?

I’m building a wordle clone. I’ve structured it so that the keypad and the letter display grid are two separate components, Keypad.js and Row,js respectively. Project structure is as follows:

src
-components
 |-Grid.js
 |-Keypad.js
 |-Row.js
-App.js
-AppContex.js
-index.js

When a user enters a letter on the keypad, initially I want that letter to appear in the first index of the first row, as per the game of wordle. How do I get Row.js to "listen" to changes in Keypad.js, so that when a user enters a letter, it shows up in the corresponding index in the grid row?

My approach so far has been to create global state using the Context API, where I’ve made an empty grid to share to the entire app:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

AppContext.js

import { createContext } from "react";

const guessRows = [
    ['', '', '', '', ''],
    ['', '', '', '', ''],
    ['', '', '', '', ''],
    ['', '', '', '', ''],
    ['', '', '', '', ''],
    ['', '', '', '', '']
]

export const AppContext = createContext()

const AppContextProvider = (props) => {
    return(
        <AppContext.Provider value = {guessRows}>
            {props.children}
        </AppContext.Provider>
    )
}

export default AppContextProvider

In Keypad.js, the letter the user enters is used to update the context (or at least that’s what I think it’s doing):

import { useContext,useState } from "react"
import { AppContext } from "../AppContext"

const Keypad = () => {
  const guessRows = useContext(AppContext);
  let currentRow = 0;
  let currentTile = 0;

  const letters = [
    "Q",
    "W",
    "E",
    "R",
   // etc
  ];

  const handleClick = (letter) => {
    guessRows[currentRow][currentTile] = letter;
    currentTile++;
    console.log("guess rows", guessRows);
  };

  return (
    <div className="keyboard-container">
      {letters.map((letter, index) => {
        return (
          <div className="key" key={index} onClick={() => handleClick(letter)}>
            {letter}
          </div>
        );
      })}
    </div>
  );
};
 
export default Keypad;

…then in Row.js, I’m looping through the context and rendering the rows:

Row.js

import { useContext } from "react";
import { AppContext } from "../AppContext";


const Row = () => {

  const rowData = useContext(AppContext)
  const currentRow = rowData[0]

  return (
    <div className="row">
      {currentRow.map((letter,index) => {
        return(
          <div className="tile" id = {index}>{letter}</div>
        )
      })}
    </div>
  )
}
 
export default Row;

Unsurprisingly this isn’t working, so any suggestions would be appreciated.

>Solution :

Your guessRows should be put into the context state, so that it can have a state setter passed down – then instead of doing guessRows[currentRow][currentTile] = letter;, call the state setter. Similarly, currentTile++; should be replaced with a state update, since this is React – the view should flow from the state.

const AppContextProvider = (props) => {
    const [guessRows, setGuessRows] = useState([
        ['', '', '', '', ''],
        ['', '', '', '', ''],
        ['', '', '', '', ''],
        ['', '', '', '', ''],
        ['', '', '', '', ''],
        ['', '', '', '', '']
    ]);
    return(
        <AppContext.Provider value = {{ guessRows, setGuessRows }}>
            {props.children}
        </AppContext.Provider>
    );
};
const { guessRows, setGuessRows } = useContext(AppContext);
const [currentRow, setCurrentRow] = useState(0);
const [currentTile, setCurrentTile] = useState(0);

const handleClick = (letter) => {
    setGuessRows(
        guessRows.map((row, i) => i !== currentRow ? row : (
            row.map((item, j) => j === currentTile ? letter : item)
        ))
    );
    setCurrentTile(currentTile + 1);
};

And then when the state setter is called, the components will re-render, including Row, which will show the changes made.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading