skip to Main Content

I have a use-case where I need to assign a unique and persistent unique id to each HTML tag in my page.

Background:

I have created a block element so everything in the webpage is a <Block /> element now.

The block element is defined like this

export const Block = ({ children, className = "", id = "", ...props }) => {
  const handleClick = () => {
    console.log("Clicks working");
  };

  const prevChildrenRef = useRef(children);

  useEffect(() => {
    if (prevChildrenRef.current !== children) {
      console.log(`Content changed for id ${id}`);
      prevChildrenRef.current = children;
    }
  }, [children, id]);

  console.log(`Rendering Block with ID: ${id}`);

  return (
    <div className={`${className} hover`} id={id} onClick={handleClick} {...props}>
      {children}
    </div>
  );
};

This is how I am using my block element, similarly in the whole page, everything is a Block at the atomic level.

usage:

<Block className="flex flex-col items-start space-y-1 text-start">
      <Block>
        <Block className="text-4xl font-bold">{start.name}</Block>
        <Block className="py-2 text-2xl">{start.headline}</Block>
      </Block>
</Block>

I would want to create a unique and persistent id for each block so even after page refresh, it would remain consistent.

What I have tried:

I tried naming every section’s block using recursion, so the top root element will have unique id as the section name, for example here, start, as we go down the children, they will be have the id <parent_id>_1, <parent_id>_2, <parent_id>_1_1 (parent’s grandchildren) and so on.

Here is the code.

export const assignIds = (element, idPrefix = prefix) => l{
  let counter = 1;

  const childrenWithIds = React.Children.map(element.props.children, (child) => {
    if (React.isValidElement(child)) {
      const newIdPrefix = `${idPrefix}_${counter}`;
      counter++;
      return React.cloneElement(
        child,
        { id: newIdPrefix, key: newIdPrefix },
        assignIds(child, newIdPrefix),
      );
    }
    return child;
  });

  return React.cloneElement(element, { id: idPrefix }, childrenWithIds);
};

While this approach works, the issue I am facing is, when the value of {start.name} or {start.headline} is changed, I only want to capture the id of that block which it is under,and not the parent or grandparent. Right now with my code implementation, it is also triggering the console – console.log(`Content changed for id ${id}`); (in the block) defined inside useEffect for parent as well as grandparents.

Where I need help

Ultimately, what I want is to capture the id of only the block which is being changed, without involving any ancestors so I can dynamically pass the CSS to change its style dynamically.

2

Answers


  1. You can solve this problem by using a combination of React’s useRef and useEffect hooks. Here’s the plan:

    1. In your Block component, create a useRef hook to store the initial children.
    2. In the useEffect hook, compare the current children with the initial children. If they are different, log the id of the current block and update the initial children ref.
    3. In your assignIds function, clone the child elements and assign them new ids based on their index in the children array. Also, pass the new id to the Block component.

    Here’s the updated code:

    import React, { useEffect, useRef } from 'react';
    
    export const Block = ({ children, className = "", id = "", ...props }) => {
      const handleClick = () => {
        console.log("Clicks working");
      };
    
      const initialChildren = useRef(children);
    
      useEffect(() => {
        if (JSON.stringify(initialChildren.current) !== JSON.stringify(children)) {
          console.log(`Content changed for id ${id}`);
          initialChildren.current = children;
        }
      }, [children, id]);
    
      console.log(`Rendering Block with ID: ${id}`);
    
      return (
        <div className={`${className} hover`} id={id} onClick={handleClick} {...props}>
          {children}
        </div>
      );
    };
    
    export const assignIds = (element, idPrefix = prefix) => {
      let counter = 1;
    
      const childrenWithIds = React.Children.map(element.props.children, (child) => {
        if (React.isValidElement(child)) {
          const newIdPrefix = `${idPrefix}_${counter}`;
          counter++;
          return React.cloneElement(
            child,
            { id: newIdPrefix, key: newIdPrefix },
            assignIds(child, newIdPrefix),
          );
        }
        return child;
      });
    
      return React.cloneElement(element, { id: idPrefix }, childrenWithIds);
    };
    

    This code will ensure that only the Block whose content has changed will log its id, not its ancestors. This is because the useEffect hook in the Block component only triggers when its own children change, not when the children of its ancestors change.

    Login or Signup to reply.
  2. To achieve your goal of capturing the ID of only the block that has changed without involving any ancestors, you can update your Block component and the assignIds function. The key is to keep track of the previous state of children within the component itself and ensure that each block only monitors its direct children.

    Here’s a revised approach:

    1. Assign unique and persistent IDs to each block using the assignIds function.
    2. Ensure that only direct children changes are detected by modifying the useEffect hook.

    Updated Block Component

    First, update your Block component to handle only direct children changes:

    import React, { useEffect, useRef } from 'react';
    
    export const Block = ({ children, className = "", id = "", ...props }) => {
      const handleClick = () => {
        console.log("Clicks working");
      };
    
      const prevChildrenRef = useRef(children);
    
      useEffect(() => {
        if (prevChildrenRef.current !== children) {
          console.log(`Content changed for id ${id}`);
          prevChildrenRef.current = children;
        }
      }, [children, id]);
    
      console.log(`Rendering Block with ID: ${id}`);
    
      return (
        <div className={`${className} hover`} id={id} onClick={handleClick} {...props}>
          {children}
        </div>
      );
    };
    

    Updated assignIds Function

    Update the assignIds function to ensure unique and persistent IDs:

    import React from 'react';
    
    export const assignIds = (element, idPrefix = 'prefix') => {
      let counter = 1;
    
      const childrenWithIds = React.Children.map(element.props.children, (child) => {
        if (React.isValidElement(child)) {
          const newIdPrefix = `${idPrefix}_${counter}`;
          counter++;
          return React.cloneElement(
            child,
            { id: newIdPrefix, key: newIdPrefix },
            assignIds(child, newIdPrefix),
          );
        }
        return child;
      });
    
      return React.cloneElement(element, { id: idPrefix }, childrenWithIds);
    };
    

    Usage Example

    Here’s how you can use the updated Block component and assignIds function in your application:

    import React from 'react';
    import { Block, assignIds } from './BlockComponent';
    
    const App = () => {
      const content = (
        <Block className="flex flex-col items-start space-y-1 text-start">
          <Block>
            <Block className="text-4xl font-bold">Start Name</Block>
            <Block className="py-2 text-2xl">Start Headline</Block>
          </Block>
        </Block>
      );
    
      const contentWithIds = assignIds(content, 'start');
    
      return <div>{contentWithIds}</div>;
    };
    
    export default App;
    

    Explanation

    1. Block Component:

      • Uses useRef to keep track of the previous children.
      • The useEffect hook checks if the current children are different from the previous children and logs the ID if they are.
    2. assignIds Function:

      • Recursively assigns unique IDs to each block element.
      • Ensures each child gets a unique ID by appending a counter to the idPrefix.
    3. Usage Example:

      • The assignIds function is called to assign IDs to the content blocks.
      • The content with assigned IDs is then rendered.

    This approach ensures that each block has a unique and persistent ID and only logs changes for direct children, without involving any ancestors.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search