0

I have a react hook.
This hook has sub-hooks, that when generated, generate for themselves an ID.
This ID is always unique, as it is simply increased by 1 each time a new sub-hook is created.

const App = () => {
  const [ idCounter, setIdCounter ] = React.useState(0);
  
  
  const genId = () => {
    setIdCounter( id => id + 1 );
    return `ID-${idCounter}`;
  }
  
  const SomeComponent = () => {
    const [ componentId, setComponentId ] = React.useState(null);
    
    React.useEffect(() => {
      let generatedId = genId();
      setComponentId( id => generatedId );
      console.log(`generated '${generatedId}'`)
    }, []);
    
    return <div>nothing works</div>
  }
  
  return <SomeComponent />
};

This loops and logs the generated id over and over again. Why on earth would it do this?
useEffect() is dependent on... nothing!! It should run only once, no?

How can I get this not to happen? I would like to be able to create several of SomeComponent from within App in the future.

2 Answers 2

2

First things first,

I would like to be able to create several of SomeComponent from within App in the future.

This (at least the way you're doing it) is not something that is possible or should be done at all when using React. You cannot create a component inside another component.

The reason your useEffect is in an infinite loop can be all sorts of things at the moment. It can be that it is not positioned in the highest scope, but my guess is that the following happens:

genId() is called, and state is updated, re-render is initialized (because of state update), and const SomeComponent = () => {...} is initialized again, thus activating useEffect again.

To fix this, first things first remove <SomeComponent /> from being created inside <App /> They should be completely separated. Secondly, pass the genId function as a prop, and add it to useEffect dependency list, since you need it there.

This would be a great start since now the code is semantically and by the rules correct.

const SomeComponent = ({ genId }) => {
  const [ componentId, setComponentId ] = React.useState(null);
    
  React.useEffect(() => {
    let generatedId = genId();
    setComponentId(generatedId);
    console.log(`generated '${generatedId}'`)
  }, [genId]);
    
  return <div>nothing works</div>
}


const App = () => {
  const [ idCounter, setIdCounter ] = React.useState(0);
  
  
  const genId = () => {
    setIdCounter( id => id + 1 );
    return `ID-${idCounter}`;
  }
  
  
  return <SomeComponent genId={genId} />
};
Sign up to request clarification or add additional context in comments.

Comments

1

I wont answer this but just make the point having sub-components have some disadvantages. You sub-componeentn is almost imposible to unit test. Maybe you can move it to top level componennt and accept generatedId as a prop

const SomeComponent = ({generatedId}) => {
    const [ componentId, setComponentId ] = React.useState(null);

    React.useEffect(() => {

      setComponentId( id => generatedId );
      console.log(`generated '${generatedId}'`)
    }, []);

    return <div>nothing works</div>
}

const App = () => {
  const [ idCounter, setIdCounter ] = React.useState(0);
  
  
  const genId = () => {
    setIdCounter( id => id + 1 );
    return `ID-${idCounter}`;
  }
  

  
  return <SomeComponent  generatedId={genId()}/>
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.