Состояние сбрасывается - PullRequest
0 голосов
/ 09 июня 2019

Новичок, чтобы Реагировать & Реактировать крючки.Я пытаюсь понять, почему мое состояние перезаписывается initialState.

Когда я ввожу значение, все работает как положено.Однако, когда я изменяю размер окна, оно сбрасывает его в initialState, и я не уверен почему.После ввода значений во входные данные updateBox отображает правильные значения.

Может кто-нибудь объяснить мне, что происходит?

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

Ответы [ 2 ]

2 голосов
/ 12 июня 2019

Проблема здесь:

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

функция updateBox , которую вы привязываете к событию окна изменения размера, будет использовать состояние, которое оно имело в момент, когда оно было ограничено (когда компонентсделал монтирование), потому что обратный вызов addEventListener выполняется в собственном закрытии.

Чтобы решить эту проблему, вы должны сохранить состояние внутри ссылки, таким образом, оно будет по-прежнему текущим, даже когдавы получаете доступ к нему из другого закрытия.поэтому сначала создайте ссылку и сохраните в ней состояние:

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

, а затем внутри функции updateBox вы прочитали макет «состояние» вместо layoutRef из 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)
  }

Дайте мне знать, если это работает, и, пожалуйста, дайте отзыв, так как это мой первый ответ здесь:)

1 голос
/ 09 июня 2019

Попробуйте переместить исходное состояние за пределы компонента или просто установить его напрямую. В вашей функции изменения размера вы устанавливаете другой объект состояния, и он повторно выполняет рендеринг компонента, но, поскольку ваше начальное состояние находится внутри компонента, я думаю, что вы просто добавляете его обратно в состояние для каждого повторного рендеринга.

Также, вероятно, не связано с вашей проблемой, вы, вероятно, захотите очистить функцию изменения размера или у вас, скорее всего, возникнут некоторые проблемы. Все сказанное попробуйте сделать что-то вроде следующего:

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

import {clone} from '../../../common/utils/clone'

const initVenueLayout = {
    venueSize:{
      width: 16,
      height: 16,
    }
  }

const VenueLayout = ({props}) => {

  const boxContainer = useRef(null);


  const [venueLayoutState,setVenueLayoutState] = useState(initVenueLayout)
  const [boxStyle,setBoxStyle] = useState({
    "height": "100%",
    "width": "90%",
  })

  useEffect(() =>{
    // State did update
  });

  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("venueLayoutState did update",venueLayoutState)
    updateBox()
  }, [venueLayoutState]);

  const updateBox = () => {
    console.log("updateBox",venueLayoutState.venueSize.width,":",venueLayoutState.venueSize.height)

    let percentage = (venueLayoutState.venueSize.height/venueLayoutState.venueSize.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 venueSizeRatioOnChange = (widthArg,heightArg) => {
    let width = (widthArg === null) ? venueLayoutState.venueSize.width : widthArg;
    let height = (heightArg === null) ? venueLayoutState.venueSize.height : heightArg;

    const cloneVenueLayoutState = clone(venueLayoutState);
    cloneVenueLayoutState.venueSize.width = width
    cloneVenueLayoutState.venueSize.height = height

    setVenueLayoutState(cloneVenueLayoutState)
  }

  const render = () => {
    console.log("render",venueLayoutState.venueSize.width,":",venueLayoutState.venueSize.height)
    // updateBox()
    const enlargeClass = (props.enlarge) ? " enlarge" : "" ;

    return (
      <div className={"venue-layout"+enlargeClass} >
        <div className="venue-layout-tools-top">
          Venue Size Ratio 
          <input 
            type="number" 
            value={venueLayoutState.venueSize.width} 
            onChange={(e) => {venueSizeRatioOnChange(e.target.value,null)}}/>
          by
          <input 
            type="number" 
            value={venueLayoutState.venueSize.height} 
            onChange={(e) => {venueSizeRatioOnChange(null,e.target.value)}}/>

          <button className="button close" onClick={props.toggleVenueLayout}>
            Close
          </button>
        </div>

        <div className="venue-layout-container">
          <div className="venue-layout-tools-side">
            side tools
          </div>
          <div className="venue-layout-box-container" ref={boxContainer}>
            <div className="venue-layout-box" style={boxStyle}>
              {boxStyle.height} x {boxStyle.width}
            </div>
          </div>
        </div>
      </div>
    );
  }

  return render();
};

export default VenueLayout;
...