简体   繁体   中英

When and in what order does React update the DOM?

I'm having trouble undestanding the exact moment that React updates the DOM.

Note: I'm using styled-components in the example below.

See this example:

CodeSandbox: https://codesandbox.io/s/elegant-dirac-j6e54

It shows a transition of the max-height property of a container.

在此输入图像描述

It workws as intended. I just don't know WHY it works.

The code:

Two lists:

  • The shortList needs 70px of height
  • The longList needs 124px of height
const shortList = ["Item 1", "Item 2", "Item 3"];
const longList = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"];

I have a styled-component container named ListContainerDIV that will handle the transition based on props . The prop name is idealHeight .

const ListContainerDIV = styled.div`
  overflow: hidden;
  max-height: ${props => props.idealHeight + "px"};
  transition: max-height ease-out .5s;
`;

App Component

function App() {

  // STATE TO KEEP LIST AND REF FOR THE CONTAINER DIV ELEMENT
  const [list, setList] = useState(shortList);
  const containerDiv_Ref = useRef(null);

  // FUNCTION TO TOGGLE BETWEEN LISTS FROM BUTTON CLICK
  function changeList() {
    setList(prevState => {
      if (prevState.length === 3) {
        return longList;
      } else {
        return shortList;
      }
    });
  }

  // CALCULATING THE IDEALHEIGHT DURING THE RENDER
  let idealHeight = null;

  if (list.length === 3) {
    idealHeight = 70;
  } else if (list.length === 6) {
    idealHeight = 124;
  }

  // RETURNING THE LISTCONTAINER WITH THE `idealHeight` PROP
  return (
    <React.Fragment>
      <button onClick={changeList}>Change List</button>
      <ListContainerDIV ref={containerDiv_Ref} idealHeight={idealHeight}>
        <ListComponent list={list} />
      </ListContainerDIV>
    </React.Fragment>
  );
}

List Component

This is a simple component to render the <ul> and the <li>'s

function ListComponent(props) {
  const listItems = props.list.map(item => <li key={item}>{item}</li>);
  return <ul>{listItems}</ul>;
}

QUESTION

How can this transition be working if I'm setting the max-height of the component even before the list is rendered? I mean, idealHeight is being set during the App render. I was thinking that longList was going to be rendered in a container that already had max-height = 124 ? And If that happened, we wouldn't be seeing a transition, right?

What is the order of events that React is running in this situation?

Apparently it updates the <ul> and the <li>'s before it updates the div from the styled-component container.

An inner component's elements will always be update before the outer component's elements?


UPDATE

Just found these tweets from Dan Abramov about this subject:

https://twitter.com/dan_abramov/status/981869076166344704

在此输入图像描述 在此输入图像描述

With regard to calculating the virtual dom, it starts by rendering the parent components, then renders the child components, then their children, and so on. All of this uses the latest values for state and props, so both App and the various child components know that the list is longer. This virtual dom is just an in-memory description of what you'd like the page to look like.

Once the virtual dom has been calculated, react compares the previous virtual dom with the new one, and finds what has changed. In this case, it finds that there's a new style that needs to be added to the div, and some new li's that need to be inserted to the ul. The exact order these are applied are an implementation detail, but they're applied synchronously and the browser cannot paint in between, so it might as well be simultaneously.

The argument passed to useState, shortList in this case, is the initial state. So when ListContainerDIV is rendered the first time, it's based on the list length being 3, so idealHeight = 70

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