如何修复反应中的限制渲染循环 [英] how to fix limit render loop in react

查看:38
本文介绍了如何修复反应中的限制渲染循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

i am currently building a chat app using react and Firebase real time database.

由于usecallback挂钩和用户状态(挂钩为addOnConnectLister addOnDisConnectListner),我在呈现最大限制方面存在问题.

i have a problem with maximum limit rendering because of usecallback hook and users state (the hooks are 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的解决方案,以获取用户状态的参考以避免该问题,但不能正常工作,回调仅敲响一次

after searching i found a solution using useRef to get a reference of users state to avoid the problem but is not working correctly the callback is runnging only once

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;

推荐答案

更改此内容:

  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;

这篇关于如何修复反应中的限制渲染循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆