Home > OS >  Unable to style component using forwardRef
Unable to style component using forwardRef

Time:02-27

I am fairly new to react, so still getting my head around the component's lifecycle. But the problem has left me scratching my head.

For instance, I do not understand why adding "setState(10);" causes style of the "Test" component to revert to it's default value yet the <div ref={ref2}>Hi</div> maintains it's style. (see imagebelow)

I am aware that "setState(10);" will cause a re-render but why is the style of the "Test" component being reverted?

Also, please ignore the "practical use" of calling setState(10) - I am aware it is pointless as it is never used, and I am aware that using "state" as a UseEffect dependency can solve this issue. But the main issue I have is understanding why the component's style reverts to it's default value.

import React, { useEffect, useState, useRef } from "react";

export default function App() {
  const [state, setState] = useState();
  let ref1 = useRef();
  let ref2 = useRef();
  useEffect(() => {
    console.log("useEffect called ", ref1.current);
    ref1.current.style.backgroundColor = "red";
    ref2.current.style.backgroundColor = "green";
    setState(10);
   // }, [state]);
  }, []);

  const Test = React.forwardRef((props, ref1) => {
    console.log("test called - rendering webpage", ref1.current);
    return (
      <div ref={ref1} {...props}>
        HI from Test{" "}
      </div>
    );
  });

  return (
    <div className="App">
      <Test ref={ref1} />
      <div ref={ref2}>Hi</div>
    </div>
  );
}

Console output

test called - rendering webpage undefined
useEffect called <div style="background-color: red;">HI </div>
test called - rendering webpage <div style="background-color: red;">HI </div>

enter image description here

CodePudding user response:

The reason the style is disappearing is that you've defined your Test component inside your App component. That means that every time App renders, you'll define a new component type named Test. The text of that component is identical to the previous one, but it's a new type as far as react can tell, so react is forced to unmount the old one, and mount the new one. This wipes out any changes you made to the old one.

So at the very least, you need to move Test outside of App. That way, the component is just defined once, and will not remount on every render

export default App() {
  // ...
}

const Test = React.forwardRef((props, ref1) => {
  // ...
})

The above should fix the reset and let you experiment with refs, but i strongly recommend that you do not use refs to style your elements. Refs are an escape hatch that's sometimes needed, but the standard way to style a component is through the style prop. If you need to change the style, then you can have a state variable and let that control the style prop.

If you manually use javascript to set ref1.current.style.backgroundColor, react has no way to know that you did this, and so can't take those changes into account. In some circumstances, react may end up overwriting your changes, or may skip making changes that it doesn't realize it needs to do.

export default function App () {
   const [colored, setColored] = useState(false);
   useEffect(() => {
     setColored(true);
   }, [])

   return (
    <div className="App">
      <Test style={colored ? { backgroundColor: "green" } : undefined} />
      <div style={colored ? { backgroundColor: "red" } : undefined}>Hi</div>
    </div>
  );
}

// Don't really need forwardRef anymore, but i left it in
const Test = React.forwardRef((props, ref) => {
  return (
    <div ref={ref} {...props}>
      HI from Test
    </div>
  );
});

CodePudding user response:

import React, { useEffect, useState, useRef } from "react";

export default function App() {
  const [state, setState] = useState();
  let ref1 = useRef();
  let ref2 = useRef();
  useEffect(() => {
    console.log("useEffect called ", ref1.current);
    ref1.current.style.backgroundColor = "red";
    ref2.current.style.backgroundColor = "green";
    setState(10);
    // }, [ref.current]);
  }, [state]);

  const Test = React.forwardRef((props, ref1) => {
    console.log("test called - rendering webpage", ref1.current);
    return (
      <div ref={ref1} {...props}>
        HI from Test{" "}
      </div>
    );
  });

  return (
    <div className="App">
      <Test ref={ref1} />
      <div ref={ref2}>Hi</div>
    </div>
  );
}

The reason this is happening is once you update the state entire component gets rerendered. Your useEffect will run only once on componentDidMount hence the new ref that you get is not updated. To get rid of this you should use state as a dependency of the useEffect.

  • Related