Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

State being reset

Newbie to React & React hooks. I'm trying to understand why my state is being overwritten by initialState.

When I input a value, things work as expected. However when I resize the window it resets it to the initialState and I'm not sure why. After inserting values into the inputs updateBox displays the correct values.

Can anybody break this down for me as to what is happening?

https://stackblitz.com/edit/react-hdf3sg


import React,{useState,useEffect,useRef} from 'react';
import './Layout.scss';

const initLayout = {
  size:{
    width: 16,
    height: 16,
  }
}
const clone = (obj) => {
  return JSON.parse(JSON.stringify(obj));
}

const Layout = ({props}) => {

  const boxContainer = useRef(null);

  const [layoutState,setLayoutState] = useState(initLayout)
  const [boxStyle,setBoxStyle] = useState({
    "height": "100%",
    "width": "90%",
  })

  useEffect(() =>{
    // State did update
    console.log("useEffect:State did update:",layoutState.size.width,":",layoutState.size.height)
    // updateBox()
  });

  useEffect(() =>{
    // Component did mount
    console.log("Component did mount")
    window.addEventListener("resize", updateBox);
    return () => {
      window.removeEventListener("resize", updateBox);
    };
  }, []);

  useEffect(() => {
    // This state did update
    console.log("layoutState did update",layoutState)
    updateBox()
  }, [layoutState]);

  const updateBox = () => {
    console.log("updateBox",layoutState.size.width,":",layoutState.size.height)

    let percentage = (layoutState.size.height/layoutState.size.width) * 100
    if(percentage > 100){
      percentage = 100
    }
    percentage = percentage+"%"

    // console.log("percentage",percentage)

    const cloneBoxStyle = clone(boxStyle)

    cloneBoxStyle.width = "100%"
    cloneBoxStyle.height = percentage
    setBoxStyle(cloneBoxStyle)

    // console.log("boxContainer",boxContainer)
    // console.log("height",boxContainer.current.clientHeight)
    // console.log("width",boxContainer.current.clientWidth)

  }



  const sizeRatioOnChange = (widthArg,heightArg) => {
    let width = (widthArg === null) ? layoutState.size.width : widthArg;
    let height = (heightArg === null) ? layoutState.size.height : heightArg;

    const cloneLayoutState = clone(layoutState);
    cloneLayoutState.size.width = width
    cloneLayoutState.size.height = height

    setLayoutState(cloneLayoutState)
  }

  const render = () => {
    console.log("render",layoutState.size.width,":",layoutState.size.height)
    // updateBox()
    // const enlargeClass = (true) ? " enlarge" : "" ;

    return (
      <div className={"layout"} >
        <div className="layout-tools-top">
          Layout Size Ratio 
          <input 
            type="number" 
            value={layoutState.size.width} 
            onChange={(e) => {sizeRatioOnChange(e.target.value,null)}}/>
          by
          <input 
            type="number" 
            value={layoutState.size.height} 
            onChange={(e) => {sizeRatioOnChange(null,e.target.value)}}/>

          <button className="button close">
            Close
          </button>
        </div>

        <div className="layout-container">
          <div className="layout-tools-side">
            <h2>Header</h2>
            <ul>

            </ul>
            <form>
              <input type="text" placeholder="Add Item" />
            </form>
          </div>
          <div className="layout-box-container" ref={boxContainer}>
            <div className="layout-box" style={boxStyle}>
              {/* {boxStyle.height} x {boxStyle.width} */}
            </div>
          </div>
        </div>
      </div>
    );
  }

  return render();
};

export default Layout;

enter image description here

like image 438
Trevor Wood Avatar asked Jun 09 '19 00:06

Trevor Wood


People also ask

What is reset state?

In a computer or data transmission system, a reset clears any pending errors or events and brings a system to normal condition or an initial state, usually in a controlled manner.

What happens when we set state?

What setState does? setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.

What happens if we update the state directly?

When you directly update the state, it does not change this. state immediately. Instead, it creates a pending state transition, and accessing it after calling this method will only return the present value. You will lose control of the state across all components.

How do I reset my state React?

To reset a component to its initial state:Store the initial state in a variable. When an event occurs, call the setState() function, passing it the initial state.


1 Answers

The problem is here:

 useEffect(() => {
    // Component did mount
    console.log("Component did mount")
    window.addEventListener("resize", updateBox); //HERE
    return () => {
      window.removeEventListener("resize", updateBox);
    };
  }, []);

the updateBox function you are binding to the resize window event will use the state it had at the moment it was bounded (when the component did mount) because the callback of addEventListener is executed in its own closure.

To solve this you have to save the state inside a ref, that way it will still be current, also when you are accessing it from another closure. so first create the ref and store the state in it:

const [layoutState,setLayoutState] = useState(initLayout);
const layoutRef= useRef({});
layoutRef.current = layoutState;

and then inside the updateBox function you have to read the layout "state" from layoutRef instead of layoutState:

  const updateBox = () => {
    let percentage = (layoutRef.current.size.height/layoutRef.current.size.width) * 100
    
    if(percentage > 100){
      percentage = 100
    }
    percentage = percentage+"%"

    const cloneBoxStyle = clone(boxStyle);

    cloneBoxStyle.width = "100%"
    cloneBoxStyle.height = percentage
    setBoxStyle(cloneBoxStyle)
  }

Let me know if it works and please give feedback since this is my first answer here :)

like image 168
brein Avatar answered Sep 19 '22 13:09

brein