繁体   English   中英

如何在反应中修复限制渲染循环

[英]how to fix limit render loop in react

我目前正在使用 react 和 Firebase 实时数据库构建一个聊天应用程序。

由于 usecallback 钩子和用户 state(钩子是 addOnConnectLister addOnDisConnectListner),我遇到了最大限制渲染的问题。

这是代码:

import React, { useEffect, useState, useCallback, useRef } from "react";
import { Icon, Menu } from "semantic-ui-react";
import firebase from "../../firebase";

const DirectMessages = (props) => {
  const [usersRef] = useState(firebase.database().ref("users"));
  const [connectedRef] = useState(firebase.database().ref(".info/connected"));
  const [presenceRef] = useState(firebase.database().ref("presence"));
  const [users, setUsers] = useState([]);

  //add presence listner
  //create a presence record for current user
  const addPressenceListner = useCallback(
    (currentUserID) => {
      connectedRef.on("value", (snap) => {
        console.log("connecting snap value", snap.val());
        if (snap.val()) {
          const ref = presenceRef.child(currentUserID);
          ref.set(true);
       ref.onDisconnect().remove((err) => {
            if (err) console.log(err);
          });
        }
      });
    },
    [connectedRef, presenceRef]
  );


  //init connected users
  const initUsers = useCallback(
    (currentUserID) => {
      const loadedUsers = [];
      usersRef.on("child_added", (snap) => {
        if (currentUserID !== snap.key) {
          let user = snap.val();
          user["uid"] = snap.key;
          user["status"] = "offline";
          loadedUsers.push(user);
          setUsers([...loadedUsers]);
        }
      });
    },
    [usersRef]
  );

  //update user status when go online
  const addOnConnectListner = useCallback(() => {
    presenceRef.on("child_added", (snap) => {
      const updatedUsers = users.map((user) => {
        if (user.uid === snap.key) {
          console.log("user connected", user);
          user["status"] = "online";
        }
        return user;
      });
      setUsers(...[updatedUsers]);
    });
  }, [presenceRef, users]);

  //update user status when go offline
  const addOnDisconnectListner = useCallback(() => {
    presenceRef.on("child_removed", (snap) => {
      const updatedUsers = users.map((user) => {
        if (user.uid === snap.key) {
          console.log("user disconnected", user);
          user["status"] = "offline";
        }
        return user;
      });
      setUsers(updatedUsers);
    });
  }, [users, presenceRef]);

  const addListners = useCallback(
    (currentUserID) => {
      addPressenceListner(currentUserID);
      initUsers(currentUserID);
      addOnConnectListner();
      addOnDisconnectListner();
    },
    [
      addOnConnectListner,
      addOnDisconnectListner,
      addPressenceListner,
      initUsers,
    ]
  );

  useEffect(() => {
    if (props.currentUser) {
      addListners(props.currentUser.uid);
    }
    return () => {
      presenceRef.off();
      usersRef.off();
      connectedRef.off();
    };
  }, [props.currentUser, addListners, connectedRef, presenceRef, usersRef]);

  const isUserOnline = (user) => user.status === "online";

  return (
    <Menu.Menu className="menu" style={{ paddingTop: "2em" }}>
      <Menu.Item>
        <span>
          <Icon name="mail" /> Direct Messages
        </span>{" "}
        ({users.length})
      </Menu.Item>

      {users.map((user) => (
        <Menu.Item key={user.uid}>
          <Icon name="circle" color={isUserOnline(user) ? "green" : "red"} />@
          {user.username}
        </Menu.Item>
      ))}
    </Menu.Menu>
  );
};

export default DirectMessages;

搜索后,我找到了一个解决方案,使用 useRef 来获取用户 state 的参考以避免该问题,但无法正常工作,回调仅运行一次

import React, { useEffect, useState, useCallback, useRef } from "react";
import { Icon, Menu } from "semantic-ui-react";
import firebase from "../../firebase";

const DirectMessages = (props) => {
  const [usersRef] = useState(firebase.database().ref("users"));
  const [connectedRef] = useState(firebase.database().ref(".info/connected"));
  const [presenceRef] = useState(firebase.database().ref("presence"));
  const [users, setUsers] = useState([]);

  //users state ref
  const getUsersRef = useRef(users);

  useEffect(() => {
    getUsersRef.current = users;
  }, [users]);

  //add presence listner
  //create a presence record for current user
  const addPressenceListner = useCallback(
    (currentUserID) => {
      connectedRef.on("value", (snap) => {
        console.log("connecting snap value", snap.val());
        if (snap.val()) {
          const ref = presenceRef.child(currentUserID);
          ref.set(true);
          ref.onDisconnect().remove((err) => {
            if (err) console.log(err);
          });
        }
      });
    },
    [connectedRef, presenceRef]
  );


  //init connected users
  const initUsers = useCallback(
    (currentUserID) => {
      const loadedUsers = [];
      usersRef.on("child_added", (snap) => {
        if (currentUserID !== snap.key) {
          let user = snap.val();
          user["uid"] = snap.key;
          user["status"] = "offline";
          loadedUsers.push(user);
          setUsers([...loadedUsers]);
        }
      });
    },
    [usersRef]
  );

  //update user status when go online
  const addOnConnectListner = useCallback(() => {
    presenceRef.on("child_added", (snap) => {
      const updatedUsers = getUsersRef.current.map((user) => {
        if (user.uid === snap.key) {
          console.log("user connected", user);
          user["status"] = "online";
        }
        return user;
      });
      setUsers(...[updatedUsers]);
    });
  }, [presenceRef, getUsersRef]);

  //update user status when go offline
  const addOnDisconnectListner = useCallback(() => {
    presenceRef.on("child_removed", (snap) => {
      const updatedUsers = getUsersRef.current.map((user) => {
        if (user.uid === snap.key) {
          console.log("user disconnected", user);
          user["status"] = "offline";
        }
        return user;
      });
      setUsers(updatedUsers);
    });
  }, [getUsersRef, presenceRef]);

  const addListners = useCallback(
    (currentUserID) => {
      addPressenceListner(currentUserID);
      initUsers(currentUserID);
      addOnConnectListner();
      addOnDisconnectListner();
    },
    [
      addOnConnectListner,
      addOnDisconnectListner,
      addPressenceListner,
      initUsers,
    ]
  );

  useEffect(() => {
    if (props.currentUser) {
      addListners(props.currentUser.uid);
    }
    return () => {
      presenceRef.off();
      usersRef.off();
      connectedRef.off();
    };
  }, [props.currentUser, addListners, connectedRef, presenceRef, usersRef]);

  const isUserOnline = (user) => user.status === "online";

  return (
    <Menu.Menu className="menu" style={{ paddingTop: "2em" }}>
      <Menu.Item>
        <span>
          <Icon name="mail" /> Direct Messages
        </span>{" "}
        ({users.length})
      </Menu.Item>

      {users.map((user) => (
        <Menu.Item key={user.uid}>
          <Icon name="circle" color={isUserOnline(user) ? "green" : "red"} />@
          {user.username}
        </Menu.Item>
      ))}
    </Menu.Menu>
  );
};

export default DirectMessages;

首先,您不需要它们,因为 state

  const [usersRef] = useState(firebase.database().ref("users"));
  const [connectedRef] = useState(firebase.database().ref(".info/connected"));
  const [presenceRef] = useState(firebase.database().ref("presence"));

将它们设置在组件之外。

const usersRef = firebase.database().ref("users")
const connectedRef = firebase.database().ref(".info/connected")
const presenceRef = firebase.database().ref("presence")

const DirectMessages = (props) => {
......

其次,删除监听器的正确方法是。

 useEffect(() => {
    if (props.currentUser) {
      addListners(props.currentUser.uid);
    return () => {
      presenceRef.off('child_added');
      presenceRef.off('child_removed');
      usersRef.off('child_added');
      connectedRef.off('value');
    };
    }

  }, [props.currentUser, addListners, connectedRef, presenceRef, usersRef]);

我将更改以下内容

  const [users, setUsers] = useState([]);

  //users state ref
  const getUsersRef = useRef(users);

  useEffect(() => {
    getUsersRef.current = users;
  }, [users]);

  const [users, _setUsers] = useState([]);
  const getUsersRef = useRef(users);

  const setUsers = (array) => {
        getUsersRef.current = array;
        _setUsers(array)
  }

最后,usecallbacks 是不必要的,您应该将它们创建为常规函数。

  const addOnDisconnectListner = () => {
    presenceRef.on("child_removed", (snap) => {
      const updatedUsers = getUsersRef.current.map((user) => {
        if (user.uid === snap.key) {
          console.log("user disconnected", user);
          user["status"] = "offline";
        }
        return user;
      });
      setUsers(updatedUsers);
    }

改变这个:

  const [users, setUsers] = useState([]);

  //users state ref
  const getUsersRef = useRef(users);

  useEffect(() => {
    getUsersRef.current = users;
  }, [users]);

对此:

  //users state ref
  const getUsersRef = useRef(users);

  useEffect(() => {
    getUsersRef.current = users;
  }, [users]);

更新钩子依赖项后:

import React, { useEffect, useState, useCallback, useRef } from "react";
import { Icon, Menu } from "semantic-ui-react";
import firebase from "../../firebase";

const usersRef = firebase.database().ref("users");
const connectedRef = firebase.database().ref(".info/connected");
const presenceRef = firebase.database().ref("presence");

const DirectMessages = (props) => {
  const [users, _setUsers] = useState([]);

  //get users ref
  const getUsersRef = useRef(users);

  const setUsers = (array) => {
    getUsersRef.current = array;
    _setUsers(array);
  };

  //add presence listner
  //create a presence record for current user
  const addPressenceListner = useCallback((currentUserID) => {
    connectedRef.on("value",(snap) => {
      console.log("connecting snap value", snap.val());
      if (snap.val()) {
        const ref = presenceRef.child(currentUserID);
        ref.set(true);
        ref.onDisconnect().remove((err) => {
          if (err) console.log(err);
        });
      }
    });
  }, []);

  //init connected users
  const initUsers = useCallback((currentUserID) => {
    const loadedUsers = [];
    usersRef.on("child_added", (snap) => {
      if (currentUserID !== snap.key) {
        let user = snap.val();
        user["uid"] = snap.key;
        user["status"] = "offline";
        loadedUsers.push(user);
        setUsers([...loadedUsers]);
      }
    });
  }, []);

  //update user status when go online
  const addOnConnectListner = useCallback(() => {
    presenceRef.on("child_added", (snap) => {
      const updatedUsers = getUsersRef.current.map((user) => {
        if (user.uid === snap.key) {
          user["status"] = "online";
        }
        console.log("user", user);
        return user;
      });
      setUsers(...[updatedUsers]);
    });
  }, []);

  //update user status when go offline
  const addOnDisconnectListner = useCallback(() => {
    presenceRef.on("child_removed", (snap) => {
      const updatedUsers = getUsersRef.current.map((user) => {
        if (user.uid === snap.key) {
          console.log("user disconnected", user);
          user["status"] = "offline";
        }
        return user;
      });
      setUsers(updatedUsers);
    });
  }, [getUsersRef]);

  const addListners = useCallback(
    (currentUserID) => {
      addPressenceListner(currentUserID);
      initUsers(currentUserID);
      addOnConnectListner();
      addOnDisconnectListner();
    },
    [
      addOnConnectListner,
      addOnDisconnectListner,
      addPressenceListner,
      initUsers,
    ]
  );

  useEffect(() => {
    if (props.currentUser) {
      addListners(props.currentUser.uid);
    }
    return () => {
      presenceRef.off();
      usersRef.off();
      connectedRef.off();
    };
  }, [props.currentUser, addListners]);

  const isUserOnline = (user) => user.status === "online";

  return (
    <Menu.Menu className="menu" style={{ paddingTop: "2em" }}>
      <Menu.Item>
        <span>
          <Icon name="mail" /> Direct Messages
        </span>{" "}
        ({users.length})
      </Menu.Item>

      {users.map((user) => (
        <Menu.Item key={user.uid}>
          <Icon name="circle" color={isUserOnline(user) ? "green" : "red"} />@
          {user.username}
        </Menu.Item>
      ))}
    </Menu.Menu>
  );
};

export default DirectMessages;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM