1

I have an array of objects in which each object has an onclick event. I map my array of objects to a component. I have a button in which I change a variable's value.

If I click my button, it changes the variable's value, but when I fire the onclick event on one of my components it does not use the variable's changed value. (I'm guessing that this is normal behaviour because the event was already binded to the component when it was created.)

Although, how can I use my updated variable value in my component's onClick event?

Edit gifted-glitter-dinlh

First I declare my variables

const [myVariable, setMyVariable] = useState("first value");
const [mymap, setmymap] = useState([
    {
      key: 1,
      name: "one",
      onClick: event => handleClick(event)
    },
    {
      key: 2,
      name: "two",
      onClick: event => handleClick(event)
    }
  ]);

setVariable is my onClick event of my first button that changes my variable's value

  const setVariable = () => {
    setMyVariable("second value");
  };

handleClick is the onClick event of my components

  const handleClick = () => {
    console.log(myVariable);
  };

Here I return my UI with my map of MyComponent

  return (
    <div>
      <button onClick={setVariable}>Set variable</button>
      <h1>{myVariable}</h1>
      {mymap.map(element => (
        <MyComponent key={element.key} onclickevent={element.onClick} />
      ))}
    </div>
  );
PouncingPoodle
  • 572
  • 1
  • 5
  • 18

3 Answers3

1

PouncingPoodle, I stuck with answering by myself. But I've just found the answer which fits perfectly for your trouble

Travnikov.dev
  • 464
  • 6
  • 14
1

In this very example storing the event in every mymap item looks redundant. If there is no reason for it then simpler version will work as expected:

import React, { useState, useCallback } from "react";

const MyComponent = ({onclickevent}) => (<div>
  <button onClick={onclickevent}>
    Use variable from MyComponent
  </button>
</div>);

const MyPage = props => {
  const [myVariable, setMyVariable] = useState("first value");

  // handler changes only when `myVariable` changes
  const clickHandler = useCallback(() => {
    console.log(myVariable);
  }, [myVariable]);

  // removing handlers from state
  const [mymap, setmymap] = useState([
    {
      key: 1,
      name: "one"
    },
    {
      key: 2,
      name: "two"
    }
  ]);

  return (
    <div>
      <button onClick={() => setMyVariable("second value")}>Set another value</button>
      <h1>{myVariable}</h1>
      {mymap.map(element => (
        <MyComponent key={element.key} onclickevent={clickHandler} />
      ))}
    </div>
  );
};

export default MyPage;

Above instead storing handler in a state and then obtaining it when iterating over state elements mymap.map(el => el.onClick) it is directly passed to children (example shows those are all the same anyway). But we also need to make sure that handler is not stale and changes depending on the value it expects:

const clickHandler = useCallback(() => {
  console.log(myVariable);
}, [myVariable]);

Updated Codesandbox example provided in a question:

Edit friendly-chaum-2pxiv

Ivar
  • 4,350
  • 2
  • 27
  • 29
0

Thanks so much @Travnikov.dev for the perfect point.

Here is my solution:

useEffect(() => {
    setmymap(prev => {
      let update = [...prev];
      for (let i = 0; i < update.length; i ++) {
        update[i].onClick = (event => handleClick(event))
        update[i] = {...update[i], onClick: update[i].onClick}
      }
      return update;
    })
  }, [myVariable]);

So what's happening is I have an useEffect with the dependency of myVariable. Once my value has changed, I just update my components' array of objects's onClick to exactly what is was, except this time the onClick will have the updated value.

PouncingPoodle
  • 572
  • 1
  • 5
  • 18