Search code examples
reactjsreact-hooksjsxreact-dom

Creating and manipulating components with react hooks


I am trying to create a home page carousel. It is basically 3 divs with articles and I want to be able to manipulate certain things, like the title's font size and placement (with margin top)

My initial code was like this:

const Home = () => {
  const [margin, setMargin] = useState('420px')
  const [font, setFont] = useState('20px')

  function mouseOver (){
    setMargin('320px')
    setFont('40px')
  }

  function mouseOut(){
    setMargin('420px')
    setFont('20px')
  }

  return(
    <div className='carousel'>
      <div className='column' id='column1' onMouseOver={mouseOver} onMouseOut={mouseOut}>
        <p className='title' id='title1' style={{marginTop: margin,fontSize: font ,color:'black'}} > Τitle 1  </p>
        <p className='markdown' id='markdown1'>mark1 </p>
      </div>
      <div className='column' id='column2' onMouseOver={mouseOver} onMouseOut={mouseOut}>
         <p className='title' id='title2' > Τitle 2</p>
         <p className='markdown' id='markdown2'> mark2</p>
      </div>
      <div className='column' id='column3'>
         <p className='title' id='title3' onMouseOver={mouseOver} onMouseOut={mouseOut}> Τitle 3 </p>
         <p className='markdown' id='markdown3'> mark3</p>
      </div>
    </div>
  )
}
  
export default Home;

Every time I hovered over any of the titles, the size of all of them changed because I use the same value and change it in each and every one of them. So I wanted to do it in a more of a react way, cause this just seemed to me like plane html with some react, so here is what I came up with.

const Home = () => {
  const [margin, setMargin] = useState('420px')
  const [font, setFont] = useState('20px')

  function mouseOver(){
    setMargin('320px')
    setFont('40px')
  }

  function mouseOut(){
    setMargin('420px')
    setFont('20px')
  }

  const arrayDivs =[]

  for (let i=0; i<=2; i++) {
    const div = React.createElement(
      'div', //type
      {key: i, className: 'column', id: `column${i+1}` }, //properties of element
      React.createElement(
        'p', //children of element
        {
          key: i,
          className: 'title',
          id: `title${i+1}`,
          style: {
            marginTop: margin,
            fontSize: font
          },
          onMouseOver: mouseOver(),
          onMouseOut:mouseOut()
        }
      ),
      `title${i+1}`
    )  
    arrayDivs.push(div)
  }

  return(
    <div className='carousel'>
      { arrayDivs }
    </div>
  )
}
  
export default Home;

But I run in to this error:

Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
at renderWithHooks (react-dom.development.js:16317:1)
at mountIndeterminateComponent (react-dom.development.js:20074:1)
at beginWork (react-dom.development.js:21587:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
at invokeGuardedCallback (react-dom.development.js:4277:1)
at beginWork$1 (react-dom.development.js:27451:1)
at performUnitOfWork (react-dom.development.js:26557:1)
at workLoopSync (react-dom.development.js:26466:1)
at renderRootSync (react-dom.development.js:26434:1)

Solution

  • The code is render looping because it is immediately invoking the handlers:

    for (let i=0; i<=2; i++){
      const div = React.createElement(
        'div', // type
        { key: i, className: 'column', id: `column${i+1}` }, // properties of element
        React.createElement(
          'p', // children of element
          {
            key: i,
            className: 'title',
            id: `title${i+1}`,
            style: { marginTop: margin, fontSize: font },
            onMouseOver: mouseOver(), // <-- immediately invoked
            onMouseOut: mouseOut()    // <-- immediately invoked
          }
        ),
        `title${i+1}`
      );  
      arrayDivs.push(div);
    }
    

    You will want to instead pass a reference to the mouseOver and mouseOut handlers:

    {
      key: i,
      className: 'title',
      id: `title${i+1}`,
      style: { marginTop: margin, fontSize: font },
      onMouseOver: mouseOver, // <-- pass reference
      onMouseOut: mouseOut    // <-- pass reference
    }
    

    This being said, using the onMouseOver and onMouseOut handlers tend to not work that well in practice. Whether or not it's considered the "React Way" I highly recommend using CSS and a :hover rule to apply the CSS styling.

    Example:

    CSS

    .column .title {
      margin-top: 420px;
      font-size: 20px;
    }
    
    .column:hover .title {
      margin-top: 320px;
      font-size: 40px;
    }
    
    .title {
      color: black;
    }
    

    Home

    const Home = () => {
      return(
        <div className='carousel'>
          <div className='column' id='column1'>
            <p className='title' id='title1'>Τitle 1</p>
            <p className='markdown' id='markdown1'>mark1</p>
          </div>
          <div className='column' id='column2'>
            <p className='title' id='title2'>Τitle 2</p>
            <p className='markdown' id='markdown2'>mark2</p>
          </div>
          <div className='column' id='column3'>
            <p className='title' id='title3'>Τitle 3</p>
            <p className='markdown' id='markdown3'>mark3</p>
          </div>
        </div>
      )
    }
    

    Edit creating-and-manipulating-components-with-react-hooks