React Router обнаружил следующую ошибку во время рендеринга. Ошибка: отрисовано меньше перехватчиков, чем ожидалось

Я пытаюсь получить ширину элемента, но получаю ошибку при вызове функции из компонента контейнера Sidebar: onClick = {() => setActiveComponent(sidebarViews[1])}. Почему возникает эта ошибка?

Боковая панель.tsx

function Sidebar() {
  const [activeComponent, setActiveComponent] = useState(PropertyList);
  const sidebarViews = [<Filters/>, <PropertyList/>]; // sidebarViews : JSX.Element[]

  return (
    <div className = "sidebar">
      <div className = "sidebar-top">
        <Navigation
          sidebarViews = {sidebarViews}
          setActiveComponent = {setActiveComponent}
        />
      </div>
      <div className = "sidebar-content">
        {activeComponent}
      </div>
    </div>        
  );
}

function Navigation({
  sidebarViews,
  setActiveComponent
}: {
  sidebarViews: JSX.Element[],
  setActiveComponent: React.Dispatch<React.SetStateAction<JSX.Element>>
}) {
  return (
    <div style = {{backgroundColor: "blue", color: "white"}}>                
      <div className = "sidebar-navigation-bar">
        <NavigationButton
          onClick = {() => setActiveComponent(sidebarViews[0])}
          icon = "tune"
          key = "0"
        ></NavigationButton>
        <NavigationButton
          onClick = {() => setActiveComponent(sidebarViews[1])}
          icon = "pin_drop"
          key = "1"
        ></NavigationButton>
      </div>
    </div>
  )
}

function NavigationButton(
  { onClick, icon }: { onClick: any, icon: string }
) {
  return (
    <span onClick = {onClick} className = "material-icons">
      {icon}
    </span>
  )
}

Список свойств

import Property from './property.tsx';
import { useCallback, useEffect, useRef, useState } from 'react';

function PropertyList() {
  const ref = useRef<HTMLHeadingElement>(null);

  useEffect(() => {
    console.info('width', ref.current ? ref.current.offsetWidth : 0);
  }, [ref.current]);

  return (
    <div className = "property-list">
      <Property key = "1"></Property>
      <Property key = "2"></Property>
    </div>
  );
}

export default PropertyList

Я получаю эту ошибку:

Ошибка, обрабатываемая React Router по умолчанию ErrorBoundary: Ошибка: Отрисовано меньше крючков, чем ожидалось. Это может быть вызвано случайным ранним заявление о возврате.

DefaultErrorComponent@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=91faa1fe:3960:15
RenderErrorBoundary@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=91faa1fe:3992:5
DataRoutes@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=91faa1fe:5154:7
Router@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=91faa1fe:4421:7
RouterProvider@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=91faa1fe:4969:7
localhost:5173:13851:25
    overrideMethod (index):13851
    overrideMethod (index):13898
    DefaultErrorComponent hooks.tsx:536
    React 14
    onClick sidebar.tsx:34
    React 23
    <anonymous> main.tsx:25 ```

Я пытаюсь установить activeComponent к компоненту PropertyList, чтобы отобразить его и получить ширину.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это происходит потому, что вы сохраняете компонент, который хотите отобразить, в состоянии. В строгом режиме React он будет отображаться дважды, чтобы выявить ошибки, при которых хуки вызываются в разном порядке.

Попробуйте это:

SideBar.tsx

const [activeComponent, setActiveComponent] = useState<'list' | 'filters'>('list');

...

<div className = "sidebar-content">
                {activeComponent === 'list' && <ProperyList />}
                {activeComponent === 'filters' && <Filters />}
</div>

Навигация

<NavigationButton onClick = {() => setActiveComponent('filters')} icon = "tune" key = "0"></NavigationButton>

<NavigationButton onClick = {() => setActiveComponent('list')} icon = "pin_drop" key = "1"></NavigationButton>
Ответ принят как подходящий

Проблема

Проблема здесь в том, что вы передаете функции перехватчику useState и функциям обновления состояния, которые интерпретируются как функции ленивой инициализации (в случае useState(PropertyList);) и обновления функционального состояния (в случае setActiveComponent(sidebarViews[0])).

Когда вы передаете функции в любом из вышеперечисленных случаев, React вызовет функцию и передаст возвращаемое значение результата для состояния. Это проблема, потому что в React мы никогда вручную или напрямую не вызываем наши функции React. Проблема в том, что при этом вызываются любые внутренние перехватчики React вне жизненного цикла компонента React.

При первоначальном рендеринге отображается компонент PropertyList, который вызывает хук useEffect. Когда состояние обновляется для рендеринга компонента Filters, оказывается, что новые хуки не вызываются, и теперь вызывается на один крючок React меньше, что приводит к ошибке.

Решение

Вы можете обновить логику, чтобы использовать функции, которые возвращают компонент React, который вы хотите сохранить в состоянии (а не результат вызова функции React). По сути, мы заменяем тип состояния с JSX.Element на React.ComponentType и используем функциональные обновления.

Пример:

Боковая панель

function Sidebar() {
  // Initializer function that returns the React function
  const [ActiveComponent, setActiveComponent] = useState<React.ComponentType>(
    () => PropertyList
  );

  // Array of function component references
  const sidebarViews = [Filters, PropertyList]; // inferred React.ComponentType[]

  return (
    <div className = "sidebar">
      <div className = "sidebar-top">
        <Navigation
          sidebarViews = {sidebarViews}
          setActiveComponent = {setActiveComponent}
        />
      </div>
      <div className = "sidebar-content">
        {/* Render component as JSX 🙂 */}
        <ActiveComponent />
      </div>
    </div>
  );
}

Навигация

function Navigation({
  sidebarViews,
  setActiveComponent,
}: {
  sidebarViews: React.ComponentType[];
  setActiveComponent: React.Dispatch<React.SetStateAction<React.ComponentType>>;
}) {
  return (
    <div style = {{ backgroundColor: "blue", color: "white" }}>
      <div className = "sidebar-navigation-bar">
        <NavigationButton
          // Functional state update to return the React function
          onClick = {() => setActiveComponent(() => sidebarViews[0])}
          icon = "tune"
          key = "0"
        />
        <NavigationButton
          // Functional state update to return the React function
          onClick = {() => setActiveComponent(() => sidebarViews[1])}
          icon = "pin_drop"
          key = "1"
        />
      </div>
    </div>
  );
}

Альтернативным решением было бы сохранить ключ, представляющий компонент, который вы хотите отобразить, во время выполнения, и вычислить этот компонент.

const componentsMap = {
  Filters,
  PropertyList,
};

Боковая панель

function Sidebar() {
  const [activeComponent, setActiveComponent] =
    useState<keyof typeof componentsMap>("PropertyList");
  const sidebarViews: (keyof typeof componentsMap)[] = [
    "Filters",
    "PropertyList",
  ];

  // Compute the component to render
  const ActiveComponent = componentsMap[activeComponent];

  return (
    <div className = "sidebar">
      <div className = "sidebar-top">
        <Navigation
          sidebarViews = {sidebarViews}
          setActiveComponent = {setActiveComponent}
        />
      </div>
      <div className = "sidebar-content">
        {/* Render component as JSX 🙂 */}
        <ActiveComponent />
      </div>
    </div>
  );
}

Навбар

function Navigation({
  sidebarViews,
  setActiveComponent,
}: {
  sidebarViews: (keyof typeof componentsMap)[];
  setActiveComponent: React.Dispatch<
    React.SetStateAction<keyof typeof componentsMap>
  >;
}) {
  return (
    <div style = {{ backgroundColor: "blue", color: "white" }}>
      <div className = "sidebar-navigation-bar">
        <NavigationButton
          onClick = {() => setActiveComponent("Filters")}
          icon = "tune"
          key = "0"
        />
        <NavigationButton
          onClick = {() => setActiveComponent("PropertyList")}
          icon = "pin_drop"
          key = "1"
        />
      </div>
    </div>
  );
}

@BushaSmith В первом комментарии вы, возможно, пропустили что-то незначительное. Я еще раз проверю свою песочницу, но у меня все работало без проблем. Для второго комментария я просто дублирую ваш код, вы также указали только компонент PropertyList с useState(PropertyList);.

Drew Reese 01.09.2024 00:58

@BushaSmith Вот работающая песочница для первого примера, а вот работающая песочница для второго примера.

Drew Reese 01.09.2024 01:12

Другие вопросы по теме