简体   繁体   中英

How to set state based off DOM node info after initial render/re-renders? (React)

In my framework I need certain information about my React component's state within the DOM after initial renders and re-renders, which I then need to pass down to child components as context .

Specifically, I need to know if the rendered DOM node is the first/last child of its parent, and keep this information as state, so I'm doing something like:

componentDidUpdate() {
  if (this.REF.current === this.REF.current.parentNode.firstChild) {
    this.setState({ isFirstChild: true });
  }
}

Calling setState here triggers a re-render, which is needed so the value can be passed to child components, which I'm doing through context - something like (stripped down):

render() {
  const contextValues = {
    ...this.context,
    ...this.state
  }

  return (
    <ContextProvider value={contextValues}>
      {this.props.children}
    </ContextProvider>
  );
}

...now in my child components I can do this.context.isFirstChild to determine whether the parent is the first-child of its own parent.

The issue...

Setting state causes a re-render. Re-rendering causes a set-state. This sets React off into an infinite loop, yielding this error:

Uncaught Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate

It seems clear to my that something about my approach is wrong. I'm just not sure what. I considered that I should't be using setState here, but then I miss out on the crucial re-rendering which passes the parent state to children as context.

Try this:

componentDidUpdate() {
  if (this.REF.current === this.REF.current.parentNode.firstChild) {
    if (!this.state.isFirstChild) {
        this.setState({ isFirstChild: true });
    }        
  }
}

This way, you're not repeatedly calling setState with the same information that your state already has in it.

The problem is every time you component updates, you are updating it again without any kind of break condition. This creates an infinite loop. You should check that this.state.isFirstChild is NOT true before updating it here.

Replace a small amount of code works for you.

      if (!this.state.isFirstChild) {
          this.setState({
              isFirstChild: true
          });
      }

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