0

I am using useContext as a global state solution. I have a Store.jsx which contains my state, and a reducer.jsx which reduces. I am using Konva to create some shapes on an HTML5 Canvas. My goal is when I click on a shape I want to update my global state with a reference to what is active, and when I click again, to clear the reference.

My Full Code can be found here: https://codesandbox.io/s/staging-platform-2li83?file=/src/App.jsx

Problem:

The problem is when I update the global state via the onClick event of a shape, its says that the reference is 'null', but when I console.log the reference in the onClick I can see the correct reference.

I think I am missing an important point to how useRef works.

This is how the flow appears in my head when I think about this:

I create a canvas, and I map an array of rectangle properties. This creates 4 rectangles. I use a wrapper component that returns a rectangle.

          {rectArray.map((rectangle, index) => {
            return (
              <RectWrapper key={index} rectangle={rectangle} index={index} />
            );
          })}

Inside the RectWrapper, I create a reference, pass it to the ref prop of the Rect. In the onclick function, when I console log 'shapeRef' I see the refence ONLY when dispatch is commented out. If I uncomment dispatch then it shows as null, and if I console log the state, the reference is always null.

const RectWrapper = ({ rectangle, index }) => {
    const shapeRef = React.useRef();

    return (
      <Rect
        x={rectangle.x + index * 100}
        y={5}
        width={50}
        height={50}
        fill="red"
        ref={shapeRef}
        onClick={() => {
          console.log("ShapeRef: ");
          console.log(shapeRef); // This correctly identifies the rect only when dispatch is uncommented
          dispatch({
            type: "active_image",
            payload: {
              index: index,
              reference: shapeRef
            }
          });
        }}
      />
    );
  };

perhaps I am going about this to wrong way with hooks. I am just trying to keep a global state of whats been clicked on because components in another file would rely on this state.

2 Answers2

2

The problem is happening because you are creating RectWrapper component as a functional component within your App component causing a new reference of the component to be created again and again and thus the reference is lost

Move your RectWrapper into a separate component declared outside of App component and pass on dispatch as a prop to it

import React, { useEffect, useContext, useState, Component } from "react";
import { Stage, Layer, Rect, Transformer } from "react-konva";
import { Context } from "./Store.jsx";
import "./styles.css";

const RectWrapper = ({ rectangle, index, dispatch }) => {
  const shapeRef = React.useRef();

  return (
    <Rect
      x={rectangle.x + index * 100}
      y={5}
      width={50}
      height={50}
      fill="red"
      ref={shapeRef}
      onClick={() => {
        console.log("ShapeRef: ");
        console.log(shapeRef); 
        dispatch({
          type: "active_image",
          payload: {
            index: index,
            reference: shapeRef
          }
        });
      }}
    />
  );
};

export default function App() {
  const [state, dispatch] = useContext(Context);

  console.log("Global State:");
  console.log(state);

  const rectArray = [
    { x: 10, y: 10 },
    { x: 10, y: 10 },
    { x: 10, y: 10 },
    { x: 10, y: 10 }
  ];

  return (
    <div className="App">
      <Stage height={500} width={500}>
        <Layer>
          {rectArray.map((rectangle, index) => {
            return (
              <RectWrapper
                dispatch={dispatch}
                key={index}
                rectangle={rectangle}
                index={index}
              />
            );
          })}
        </Layer>
      </Stage>
    </div>
  );
}

Working demo

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
0

I don't think you need to create a ref in RectWrapper, because onClick has one event parameter. And the ref of the element that was clicked can be found in the event:

onClick={(e) => {
    const thisRef = e.target;
    console.log(thisRef );
    ...

Here is a working version without useRef: https://codesandbox.io/s/peaceful-brook-je8qo

HermitCrab
  • 3,194
  • 1
  • 11
  • 10