0

I need React App level access via refs to my functions. Specifically, Square > updateVal & updateColor. The hierarcy being App > Grid > Row x6 > Square x5. For whatever reason my useRef([]) array remains empty when it should contain 30 references (one to each Square). Hopefully the codebase will clarify my ask!

A secondary question of lesser importance: Sometimes my grid fails to display upon refreshing the page... if I change any of the text that's expected to render, the app finishes loading completely. I assume now that I've started to work with a useEffect() my issue might be related. Let me know if you notice anything that might relate to that issue as well.

// index.js
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
// App.js
import React from 'react'
import Row from './Components/Row/Row';
import Square from './Components/Square/Square';
import { useEffect, useState, useRef } from 'react';
import Grid from './Components/Grid/Grid';

let hunch = ''

function App() {
  const [hurdle, setHurdle] = useState('')
  const [flag, setFlag] = useState('')
  const MAX_GUESSES = 6
  const MAX_WORD_LENGTH = 5
  let guess = 0
  let g = [...new Array(MAX_GUESSES)].map(a => new Array(MAX_WORD_LENGTH))
  let hunchCharOccurrences = {}

  const refs = useRef([])
  let rows = new Array(MAX_GUESSES)

  function handleEvent(event){
    // ...
    // OMITTED FOR BREVITY
    // ...
    let id = (MAX_GUESSES * guess) + hunch.length
    if( refs && "current" in refs){
      refs?.current[id].updateVal(k.toUpperCase())
      refs?.current[id].setColor('palevioletred')
    }
    event.preventDefault()
  }

  function listen(){
    document.addEventListener('keydown', handleEvent)
  }

  useEffect(() => {
    refs.current = refs.current.slice(0, MAX_GUESSES * MAX_WORD_LENGTH)

    let idIndex = 0
    for(let i = 0; i < MAX_GUESSES; i++){
      let squares = new Array(5);
      for(let j = 0; j < MAX_WORD_LENGTH; j++){
        squares[j] = <Square key={idIndex} ref={el => refs.current[idIndex] = el} />
        idIndex++
      }
      rows[i] = squares
    }

    return listen()
  }, [])

  return (
    <div className="App">
      <main>
        <div className='rendered-grid-container'>
          <Grid rows={rows} />
        </div>
        <br/>
      </main>
    </div>
  );
}

export default App;
// src>Components>Grid.js
import React, { useState } from 'react'
import Row from '../Row/Row'

function Grid({rows}, ref){
  const gridItems = rows.map((row, index) =>
    <tbody key={index}>
      <Row squares={row} />
    </tbody>
  )
  return (
    <table className='grid-container'>
      { gridItems }
    </table>
  )
}

Grid.displayName = `Grid`
export default  React.forwardRef(Grid)
// src>Components>Grid.js
import React, { useState } from 'react'
import Square from '../Square/Square'

function Row({squares}, ref){
  const rowItems = squares.map((square, index) => 
    <td key={index}>
      {square}
    </td>
  )

  return (
    <tr className='row-container'>
      { rowItems }
    </tr>
  )
}

Row.displayName = `Row`
export default React.forwardRef(Row)
// src>Components>Square.js
import React, { useState, useImperativeHandle } from 'react'

function Square(props, ref) { // anonymouus -> named function
  const [val, setVal] = useState('')
  const [color, setColor] = useState('aqua')

  function updateVal(v){
    setVal(v)
  }

  function updateColor(c){
    setColor(c)
  }

  useImperativeHandle(
    ref,
    () => {
      return {
        updateVal: updateVal,
        updateColor: updateColor
      }
    },
  )

  return (
    <div className='square-container'>
      <div className='square' ref={ref} style={{backgroundColor: color}}>
        { val }
      </div>
    </div>
  )
}

Square.displayName = `Square`
export default React.forwardRef(Square) //forwardRef HOC

I'm referencing the following posts as implementation guides:


I'm aware that common React convention is passing data from children to their parent components. In addition to fixing my error, I'd appreciate some clarification on using functions for forwarded ref HOC's like mine. After all, my app itself is a F(x).

trevor4n
  • 281
  • 1
  • 3
  • 11
  • If you want to share state between components, a much easier way to handle it is by using a context. Store the value and color in a 2d array in your context, then just pass down cordinates to the children so they know which index in the array to update. – super May 11 '22 at 07:13
  • @super for this project in specific, I'd like the square components to track their own color & value (accessible from App logic). Thanks for the suggestion tho! – trevor4n May 11 '22 at 07:24
  • 1
    Be my guest. Just seems a bit wierd to use a hammer for a screw. This is not the kind of problem refs and forwardrefs are meant to solve. – super May 11 '22 at 07:27
  • Does this answer your question? [Call child method from parent](https://stackoverflow.com/questions/37949981/call-child-method-from-parent) – pilchard May 11 '22 at 08:32
  • You're using a single ref which you pass to `useImperativeHandle` which I would guess is overwriting `current` not adding to it, you'll need to use a ref per square (array of refs vs a ref holding an array) – pilchard May 11 '22 at 08:35

0 Answers0