javascript 组件DidUpdate()内部的setState()

6tqwzwtp  于 2022-12-21  发布在  Java
关注(0)|答案(9)|浏览(147)

我正在写一个脚本,根据下拉菜单的高度和输入在屏幕上的位置将下拉菜单移到输入的下方或上方。我还想根据下拉菜单的方向设置修改器。但是在componentDidUpdate内部使用setState会创建一个无限循环(这是显而易见的)
我已经找到了一个使用getDOMNode并直接将classname设置为dropdown的解决方案,但是我觉得应该有一个使用React工具的更好的解决方案。有人能帮助我吗?
下面是使用getDOMNode的部分工作代码(为了简化代码,稍微忽略了定位逻辑)

let SearchDropdown = React.createClass({
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        el.classList.remove('dropDown-top');
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            el.classList.add('dropDown-top');
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        return (
            <DropDown >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});

这是setstate的代码(创建一个无限循环)

let SearchDropdown = React.createClass({
    getInitialState() {
        return {
            top: false
        };
    },
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        if (this.state.top) {
           this.setState({top: false});
        }
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            if (!this.state.top) {
              this.setState({top: true});
           }
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        let class = cx({'dropDown-top' : this.state.top});
        return (
            <DropDown className={class} >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});
kq0g1dla

kq0g1dla1#

你可以在componentDidUpdate中使用setState,问题是你创建了一个无限循环,因为没有break条件。
基于这样一个事实,即一旦呈现组件,您就需要浏览器提供的值,我认为您使用componentDidUpdate的方法是正确的,它只需要更好地处理触发setState的条件。

6l7fqoea

6l7fqoea2#

componentDidUpdate的签名是void::componentDidUpdate(previousProps, previousState)。使用这个签名,您将能够测试哪些属性/状态是脏的,并相应地调用setState

示例:

componentDidUpdate(previousProps, previousState) {
    if (previousProps.data !== this.props.data) {
        this.setState({/*....*/})
    }
}
bkhjykvo

bkhjykvo3#

如果您在componentDidUpdate中使用setState,它将更新组件,导致调用componentDidUpdatecomponentDidUpdate随后再次调用setState,从而导致无限循环。您应该有条件地调用setState,并确保违反调用的条件最终发生,例如:

componentDidUpdate: function() {
    if (condition) {
        this.setState({..})
    } else {
        //do something else
    }
}

如果您只是通过向组件发送属性来更新组件(它不是通过setState更新的,componentDidUpdate内部的情况除外),则可以在componentWillReceiveProps中调用setState,而不是componentDidUpdate

gzjq41n4

gzjq41n44#

此示例将帮助您了解React生命周期挂接
您可以在getDerivedStateFromProps方法中使用setState,即static,并在componentDidUpdate中的属性发生变化后触发该方法。
componentDidUpdate中,您将获得从getSnapshotBeforeUpdate返回的第三个参数。
您可以检查此codesandbox link

// Child component
class Child extends React.Component {
  // First thing called when component loaded
  constructor(props) {
    console.log("constructor");
    super(props);
    this.state = {
      value: this.props.value,
      color: "green"
    };
  }

  // static method
  // dont have access of 'this'
  // return object will update the state
  static getDerivedStateFromProps(props, state) {
    console.log("getDerivedStateFromProps");
    return {
      value: props.value,
      color: props.value % 2 === 0 ? "green" : "red"
    };
  }

  // skip render if return false
  shouldComponentUpdate(nextProps, nextState) {
    console.log("shouldComponentUpdate");
    // return nextState.color !== this.state.color;
    return true;
  }

  // In between before real DOM updates (pre-commit)
  // has access of 'this'
  // return object will be captured in componentDidUpdate
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate");
    return { oldValue: prevState.value };
  }

  // Calls after component updated
  // has access of previous state and props with snapshot
  // Can call methods here
  // setState inside this will cause infinite loop
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("componentDidUpdate: ", prevProps, prevState, snapshot);
  }

  static getDerivedStateFromError(error) {
    console.log("getDerivedStateFromError");
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.log("componentDidCatch: ", error, info);
  }

  // After component mount
  // Good place to start AJAX call and initial state
  componentDidMount() {
    console.log("componentDidMount");
    this.makeAjaxCall();
  }

  makeAjaxCall() {
    console.log("makeAjaxCall");
  }

  onClick() {
    console.log("state: ", this.state);
  }

  render() {
    return (
      <div style={{ border: "1px solid red", padding: "0px 10px 10px 10px" }}>
        <p style={{ color: this.state.color }}>Color: {this.state.color}</p>
        <button onClick={() => this.onClick()}>{this.props.value}</button>
      </div>
    );
  }
}

// Parent component
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: 1 };

    this.tick = () => {
      this.setState({
        date: new Date(),
        value: this.state.value + 1
      });
    };
  }

  componentDidMount() {
    setTimeout(this.tick, 2000);
  }

  render() {
    return (
      <div style={{ border: "1px solid blue", padding: "0px 10px 10px 10px" }}>
        <p>Parent</p>
        <Child value={this.state.value} />
      </div>
    );
  }
}

function App() {
  return (
    <React.Fragment>
      <Parent />
    </React.Fragment>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
cwtwac6a

cwtwac6a5#

我会说,您需要检查state是否已经具有您试图设置的相同值,如果是相同的,则没有必要为相同的值再次设置state。
请确保按如下方式设置您的状态:

let top = newValue /*true or false*/
if(top !== this.state.top){
    this.setState({top});
}
5jvtdoz2

5jvtdoz26#

当循环中没有中断条件时,在ComponentDidUpdate中使用此. setState会创建无限循环。您可以使用redux在if语句中设置变量true,然后在条件中设置变量false,这样它就可以工作。
就像这样。

if(this.props.route.params.resetFields){

        this.props.route.params.resetFields = false;
        this.setState({broadcastMembersCount: 0,isLinkAttached: false,attachedAffiliatedLink:false,affilatedText: 'add your affiliate link'});
        this.resetSelectedContactAndGroups();
        this.hideNext = false;
        this.initialValue_1 = 140;
        this.initialValue_2 = 140;
        this.height = 20
    }
ftf50wuq

ftf50wuq7#

我遇到过类似的问题。请将componentDidUpdate设置为一个箭头函数。应该可以。

componentDidUpdate = (params) => {
    let el = this.getDOMNode();
    if (this.state.top) {
       this.setState({top: false});
    }
    if(needToMoveOnTop(el)) {
        el.top = newTopValue;
        el.right = newRightValue;
        if (!this.state.top) {
          this.setState({top: true});
       }
    }
}
2jcobegt

2jcobegt8#

我遇到过一个类似的问题,我必须将工具提示居中。componentDidUpdate中的React setState确实让我陷入了无限循环,我尝试了它工作的条件。但我发现在ref回调中使用给了我更简单和干净的解决方案,如果您使用内联函数进行ref回调,则每次组件更新都将面临null问题。因此,在ref回调中使用函数引用并在那里设置状态。这将启动重新渲染

rjzwgtxy

rjzwgtxy9#

您可以在componentDidUpdate内使用setState

相关问题