简体   繁体   中英

Problem switching to functional component react with useEffect

I'm currently trying to change a class component (working) into a functional component (not working). Basically, my goal is to make the element centered at the page.

EDIT: If anyone can comment what ref={tc=>(this.treeContainer=tc)} does, it will be extremely helpful too!

My class component (that works):

/** 
 * Component returning a div containing a tree
 * Need to declare as class for ComponentDidMount() 
 */
class CenterMe extends React.PureComponent {

    state={}

    /* Used to center the tree on render */
    componentDidMount() {
      const dimensions = this.treeContainer.getBoundingClientRect();
      this.setState({
        translate: {
          x: 30,
          y: dimensions.height / 2
        }
      });
    }

    /* Returns the actual tree content */
    render(){
      return(
          <div class="treeWrapper" ref={tc => (this.treeContainer = tc)}>
              <Tree
                  translate = {this.state.translate}
              />
          </div>
      );
    }
}

My current functional component look like this and does not work:

function CenterMe() {
    const [state, setState] = useState({translate:{x:0,y:0}); // Added thanks to @jstu

    /* Used to center the tree on render */
    useEffect(()=> {
      const dimensions = treeContainer.getBoundingClientRect();
      setState({
        translate: {
          x: 30,
          y: dimensions.height / 2
        }
      });
    });

    /* Returns the actual tree content */
      return(
          <div class="treeWrapper" ref={tc => (this.treeContainer = tc)}>
              <Tree
                  translate = {this.state.translate}
              />
          </div>
      );
}

More specifically, it is complaining that setState and treeContainer are not defined. If it matters, the Tree component is imported from react-d3-tree and the translate function basically does quite literally what it says - it moves the tree. I think the problem is mainly focused on ref={tc=>(this.treeContainer=tc)} , so explanation on that can also help. Thank you so much!

You need to use add two hooks useRef and useState in order to make the conversion

function CenterMe() {
 const [translate, setTranslate] = useState({ x: 0, y: 0 });
 const treeContainer = useRef(null)

 /* Used to center the tree on render */
 useEffect(()=> {
  const dimensions = treeContainer.current.getBoundingClientRect();
  setTranslate({
      x: 30,
      y: dimensions.height / 2
  });
 }, []);

/* Returns the actual tree content */
  return(
      <div class="treeWrapper" ref={treeContainer}>
          <Tree
              translate={translate}
          />
      </div>
  );
}

The useState is to replace the setState from the class based component and the useRef will give you the current reference if you access the treeContainer.current key.

To explain for what is the ref part, basically the ref in your case is referencing a dom node, for example if your tree component looks like this:

<div className="awesome-class">
 <p>Hi</p>
</div>

what the use ref is doing here is that it will give you the dom element ref which would look like this assuming the tree component is like the above example.

    <div class="treeWrapper">
     <div className="awesome-class">
      <p>Hi</p>
     </div>
    </div>

With the ref you are getting the element from the dom, for example in your specific case getBoundingClientRect will give you the size of that specific element, this is very useful as you can see because you're able to use the children values in the parent component. More info of using the hook here: https://reactjs.org/docs/hooks-reference.html#useref

So you seem to have missed the state hook. Try this

    function CenterMe() {
    const [state, setState] = useState({translate:{x:0,y:0}}); // Set an initial state
    const treeContainer = useRef(null);

        /* Used to center the tree on render */
        useEffect(()=> {
          const dimensions = treeContainer.current.getBoundingClientRect();
          setState({
            translate: {
              x: 30,
              y: dimensions.height / 2
            }
          });
        },[]);

        /* Returns the actual tree content */
          return(
              <div class="treeWrapper" ref={treeContainer}>
                  <Tree
                      translate = {state.translate}
                  />
              </div>
          );
    }

I haven't tested this. but what you are missing is the useState hook. Also just noticed that you mean to implement componentDidMount. so you need to pass an empty array at the end. So that it behaves as componentDidMount

Ok, so a couple of things. Use state returns an array which can be deconstructed with syntax []. Like this

    const [someName, setSomeName] = useState({
        translateX: 10,
        translateY: 20 
    })

You are giving useState an anonymus function. I dont know if you can do that, never done it before.

But try the the solution i wrote above. Set value for translatioY by calling that method and then in Tree component do this.

<Tree  translate = {someName}/>

And for ref you have to use useRef() hook

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM