React函数组件setTimeout-多个渲染调用和重渲染(推荐方法)(多次触发)

时间:2019-04-19 14:24:02

标签: javascript reactjs

我有一个Notification组件,该组件应在几秒钟后关闭并调用onClose属性:

function Notification(props) {
  console.log("Notification function component called");
  setTimeout(() => {
    props.onClose();
  }, 4000);
  return (
    <div>
      {props.children}
      <button onClick={props.onClose}>Close</button>
    </div>
  );
}

在我的App中,我有一个保存通知对象的状态,并通过它们进行映射。

class App extends React.Component {
  constructor() {
    super();
    this.pushNotification = this.pushNotification.bind(this);
  }
  state = {
    notifications: {}
  };
  pushNotification() {
    const id = uuid();
    const newNotifications = { ...this.state.notifications };
    const date = new Date();
    newNotifications[id] = {
      id,
      date: JSON.stringify(date)
    };
    this.setState({
      notifications: newNotifications
    });
  }
  removeNotification(id) {
    console.log("removeNotification");
    const newNotifications = { ...this.state.notifications };
    delete newNotifications[id];
    this.setState({
      notifications: newNotifications
    });
  }
  render() {
    return (
      <div className="App">
        <button onClick={this.pushNotification}>Push notification</button>
        {Object.keys(this.state.notifications).map(
          (notificationIndexKey, index) => {
            return (
              <Notification
                originalKey={JSON.stringify(index)}
                key={notificationIndexKey}
                onClose={() => {
                  console.log("Notfication fired on close");
                  this.removeNotification(notificationIndexKey);
                }}
              >
                Notification{" "}
                {this.state.notifications[notificationIndexKey].date}
              </Notification>
            );
          }
        )}
      </div>
    );
  }
}

我注意到,如果我在状态中推送多个通知,则setTimout会被初始化多次(这很有意义,因为每次状态更新时都会调用render)

我的问题是,您将如何建议对此进行优化,以使超时仅被调用一次。

我尝试过的一种方法是使用删除的项目创建一个数组,并在调用prop之前进行检查。

此处的沙盒:https://codesandbox.io/s/6y3my2y2jr

2 个答案:

答案 0 :(得分:2)

在安装组件后,应应用该副作用。 目前,您的代码将在渲染上执行此操作。 渲染函数可以被多次调用。

此代码应反映正确的更改。

class Notification extends React.Component {
  componentDidMount() {
    setTimeout(this.props.onClose, 4000);
  }
  render() {
    return (
      <div>
       {props.children}
       <button onClick={props.onClose}>Close</button>
      </div>
    );
  }
}

答案 1 :(得分:1)

您可以通过将类属性设置为notificationTimer初始设置为null来完成此操作,并可以将Notification函数修改为:

function Notification(props) {
  console.log("Notification function component called");

  if (!this.notificationTimer)
    this.notificationTimer = setTimeout(() => {
      props.onClose();
    }, 4000);
  }

  return (
    <div>
      {props.children}
      <button onClick={props.onClose}>Close</button>
    </div>
  );
}

在关闭函数中,您可以执行以下操作:

onClose() {
  // Your code.

  if (this.notificationTimer) {
    clearTimeout(this.notificationTimer);
    this.notificationTimer = null;
  }
}

这不会让您创建多个计时器。