useEffect 和 SocketIO Infinite Rerendering,为什么会这样?

useEffect and SocketIO Infinite Rerendering, why is it happening?

我正在使用来自套接字的 useEffect Hook 获取数据。每次我收到响应时,一个特定的组件都会重新呈现,但我不会将任何套接字数据传递给该组件。

代码:

  const App = () => {
  const isMounted = React.useRef(false);
  const [getSocketData, setSocketData] = useState([]);

  useEffect(() => {
    console.log('[App.js] Mounted!');
    isMounted.current = true;
    const socket = socketIOClient(process.env.REACT_APP_BOARD);
    socket.on('ServersInfo', (data) => {
      if (isMounted.current) {
        setSocketData([...data]);
      }
    });
    socket.on('connect_error', () => {
      if (isMounted.current) {
        setSocketData([]);
      }
      console.log('Connection error!');
    });
    return (() => {
      isMounted.current = false;
      socket.disconnect();
      console.log('[App.js] unmounted');
    });
  }, []);

  const routes = (
    <Switch>
      <Route path={['/', '/users/:id']} exact component={() => <MainPage axiosInstance={axiosInstance} />} />
      <Route path='/servers' render={(spProps) => <ServerPing {...spProps} />} />
      <Route render={(nfProps) => <NotFoundComponent {...nfProps} />} />
      {/* <Redirect to='/' /> */}
    </Switch>
  );
  return (
    <div className="App">
      <Layout>
        <Suspense fallback={<p>Loading...</p>}>
          {routes}
        </Suspense>
      </Layout>
    </div>
  );
};

export default App;

我的组件是什么样子的: - App.js - 布局(不重新渲染)(有 3 children)- MainPage(无限重新渲染),ServerPing(不重新渲染),NotFoundComponent(不重新渲染)

问题是:为什么 MainPage 组件会无限重新渲染? 我的意思是 MainPage 组件及其 children 在获取套接字数据时卸载并再次挂载,这是一种奇怪的行为。

主页组件:

const MainPage = ({ axiosInstance, ...props }) => {

  const isMounted = React.useRef(false);
  const [loadingPage, setLoadingPage] = useState(true);
  const [usernames, setUsernames] = useState([]);
  const [currentDay] = useState(new Date().getDay());

  useEffect(() => {
    isMounted.current = true;
    console.log('[MainPage.js] Mounted!');
    getUsers();
    return () => {
      console.log('[MainPage.js] Unmounted!');
      isMounted.current = false;
    };
  }, []);

  const getUsers = async () => {
    try {
      const res = await axiosInstance.get('/users');
      const newData = await res.data;
      const newArray = [];
      newData.map(user => (
        newArray.push({id: user._id, flag: user.flag, text: user.name, value: user.name.toLowerCase()})
      ));
      if (isMounted.current) {
        setUsernames(newArray);
        setLoadingPage(false);
      }
    } catch {
      if (isMounted.current) {
        setLoadingPage(false);
      }
    }
  };

  return...

问题是您使用的是 component 道具而不是 render 道具 render the MainPage 如果给定回调,它将在 App 组件的任何重新渲染时重新安装该组件到组件道具。

下面的代码应该可以解决这个问题

<Route 
    path={['/', '/users/:id']} 
    exact 
    render={(props) => <MainPage {...props} axiosInstance={axiosInstance} />} 
/>

根据 react-router-dom 文档

When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).