useEffect и SocketIO Infinite Rerendering, почему это происходит? - PullRequest
1 голос
/ 30 апреля 2019

Я получаю данные, используя 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 - Layout (не перерисовывается) (у которого есть 3 дочерних элемента) - MainPage (перерисовывается бесконечно), ServerPing (не перерисовывается), NotFoundComponent (не перерисовывается)

Вопрос: почему компонент MainPage перерисовывается бесконечно?Я имею в виду, что компонент MainPage и его дочерние элементы демонтируются и монтируются снова, когда данные сокета извлекаются, что является странным поведением.

MainPageComponent:

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...

1 Ответ

1 голос
/ 30 апреля 2019

Проблема в том, что вы используете component prop вместо render prop в render the MainPage, что перемонтирует компонент при любом повторном рендеринге компонента App, если существует обратный вызов, данный компоненту prop.

Ниже код должен решить проблему

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

В соответствии с документами о реакции маршрутизатора-домена

Когда вы используете компонент (вместо render или children, ниже), маршрутизатор использует React.createElement для создания нового элемента React из данный компонент. Это означает, что если вы предоставляете встроенную функцию для компонент prop, вы будете создавать новый компонент каждый рендер. это приводит к размонтированию существующего компонента и нового компонента монтирование вместо простого обновления существующего компонента. Когда используешь встроенную функцию для встроенного рендеринга, используйте render или children опора (ниже).

...