skip to Main Content

I have a list, containing some jsx components like components:

components = [
  <span className="c1 c2" onClick={() => console.log("test")}>...</span>,
  <span className="c1 c2" onClick={() => console.log("test")}>...</span>,
  <span className="c1 c2" onClick={() => console.log("test")}>...</span>,
]

BTW the components, is made with pushing into array:

for(...) {
  components.push(<span ...>...</span>)
}

and i use this list as below:

const MyList = () => {
  return (
    <p>{components}</p>
  )
}

the problem is that, after rendering, items don’t have any onClick (or onclick event handler) function.
although they have class property, and the event is set to function noop() {}.
Browsers inspector shows them like:

// there is no 'onclick' function
<span class="c1 c2">...</span>
...

New problem:
Suppose that c2 class is like:

.c2{
  color: blue;
}
.c2:hover {
  cursor: pointer;
}

It changes color of text to blue, but it doesn’t show pointer on hover(doesn’t do anything on hover), too.

I appreciate any help.

2

Answers


  1. Event handlers like onClick only work when elements are created and rendered within React’s rendering process (as in the "render" in class components). If you create and store JSX elements in an array outside the component’s lifecycle (like in your example), React won’t properly attach event handlers to those elements.

    You should do this instead

    const MyList = () => {
      // Define different functions
      const handleClick1 = () => console.log("Clicked item 1");
      const handleClick2 = () => console.log("Clicked item 2");
      const handleClick3 = () => console.log("Clicked item 3");
    
      // Array of data and corresponding event handlers
      const items = [
        { text: "Item 1", onClick: handleClick1 },
        { text: "Item 2", onClick: handleClick2 },
        { text: "Item 3", onClick: handleClick3 },
      ];
    
      return (
        <div>
          {items.map((item, index) => (
            <span
              key={index}
              className="c1 c2"
              onClick={item.onClick} // Assign specific onClick handler
            >
              {item.text}
            </span>
          ))}
        </div>
      );
    };
    
    export default MyList;

    The case above assumes that your components are rendering only span tags. If they were dynamic, say you want to render a div, a button and a p tag, I would do this instead – create the array to have objects with the elementType and props

    import React from "react";
    
    const DynamicComponents = () => {
      const components = [
        {
          elementType: "div",
          props: {
            className: "box",
            onClick: () => console.log("Div clicked"),
            children: "I am a Div",
          },
        },
        {
          elementType: "button",
          props: {
            className: "btn",
            onClick: () => console.log("Button clicked"),
            children: "Click Me",
          },
        },
        {
          elementType: "p",
          props: {
            className: "text",
            onClick: () => console.log("Paragraph clicked"),
            children: "I am a Paragraph",
          },
        },
      ];
    
      return (
        <div>
          {components.map((component, index) => {
            const { elementType, props } = component;
            const Element = elementType; // React allows you to use a string to represent an element type dynamically
            return <Element key={index} {...props} />;
          })}
        </div>
      );
    };
    
    export default DynamicComponents;
    Login or Signup to reply.
  2. There is no problem creating an array of elements with onClick. When the elements are rendered, the click handlers will work properly. In the demo below, click each letter to see the corresponding handler respond –

    const components = [
      <span
        className="c1 c2"
        children="A"
        key="A"
        onClick={() => console.log("test A")} />,
      <span
        className="c1 c2"
        children="B"
        key="B"
        onClick={() => console.log("test B")} />,
      <span
        className="c1 c2"
        children="C"
        key="C"
        onClick={() => console.log("test C")} />,
    ]
    
    const MyList = () => {
      return (
        <p>{components}</p>
      )
    }
    
    ReactDOM.createRoot(document.querySelector("#app")).render(<MyList />)
    p { display: flex; flex-direction: row; gap: 1rem; }
    span { display: block; padding: 0.5rem; background-color: #fc0; }
    span:hover { opacity: 0.8; }
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <div id="app"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search