Можете ли вы вернуться раньше времени с хуками React?

Документы React ясно дают понять, что вызов хуков условно не будет работать. Из оригинальной презентации React hooks, причина в том, что React использует порядок, в котором вы называете хуки, для ввода правильного значения.

Я понимаю это, но теперь мой вопрос заключается в том, можно ли досрочно возвращаться из функционального компонента с хуками.

Так что-то вроде этого разрешено?:

import React from 'react';
import { useRouteMatch, Redirect } from 'react-router';
import { useSelector } from 'react-redux';

export default function Component() {
  const { match } = useRouteMatch({ path: '/:some/:thing' });
  if (!match) return <Redirect to = "/" />;

  const { some, thing } = match.params;
  const state = useSelector(stateSelector(some, thing));

  return <Blah {...state} />;
}

Технически, хук useSelector вызывается условно, однако порядок их вызова не меняется между рендерами (хотя возможно, что будет вызываться на один хук меньше).

Если это не разрешено, можете ли вы объяснить Зачем, что это не разрешено, и предоставить общие альтернативные подходы к раннему возврату в функциональном компоненте с хуками?

У вас есть ответ на свой вопрос - нельзя, потому что React uses the order you call hooks to inject the correct value. На самом деле это может сработать в вашем случае, и предупреждение, которое вы получите, будет просто предупреждением, а не ошибкой. Но вы можете столкнуться с ошибкой позже, когда фактически забудете о местонахождении этого компонента и решите добавить больше хуков или переставить условное выражение.

jayarjo 02.03.2019 08:27
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
29
1
12 097
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

Invariant Violation: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.

React не может определить разницу между ранним возвратом перед вызовом ловушки и вызовом ловушки, который является условным по какой-то другой причине. Например, если у вас есть 3 вызова useState и вы иногда возвращаетесь после второго, React не может определить, вернулись ли вы после второго вызова useState или вы поставили условие вокруг первого или второго вызова useState, поэтому он может' t надежно знает, возвращает ли он правильное состояние для двух вызовов useState, которые происходят сделал.

Вот пример, который вы можете использовать, чтобы увидеть эту ошибку в действии (дважды нажмите кнопку «Увеличить состояние 1», чтобы получить ошибку):

import React from "react";
import ReactDOM from "react-dom";

function App() {
  const [state1, setState1] = React.useState(1);
  if (state1 === 3) {
    return <div>State 1 is 3</div>;
  }
  const [state2, setState2] = React.useState(2);
  return (
    <div className = "App">
      <div>State 1: {state1}</div>
      <div>State 2: {state2}</div>
      <button onClick = {() => setState1(state1 + 1)}>Increment State 1</button>
      <button onClick = {() => setState2(state2 + 1)}>Increment State 2</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit Early return with hooks

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

В случае с моим примером это могло бы выглядеть следующим образом:

import React from "react";
import ReactDOM from "react-dom";

const AfterEarlyReturn = ({ state1, setState1 }) => {
  const [state2, setState2] = React.useState(2);
  return (
    <div className = "App">
      <div>State 1: {state1}</div>
      <div>State 2: {state2}</div>
      <button onClick = {() => setState1(state1 + 1)}>Increment State 1</button>
      <button onClick = {() => setState2(state2 + 1)}>Increment State 2</button>
    </div>
  );
};
function App() {
  const [state1, setState1] = React.useState(1);
  if (state1 === 3) {
    return <div>State 1 is 3</div>;
  }
  return <AfterEarlyReturn state1 = {state1} setState1 = {setState1} />;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit Early return with hooks

это хорошая идея, но она недостаточно чистая, поэтому я написал этот HOC, чтобы сделать ее чище

mh-alahdadian 05.09.2019 22:44

Совершенно супер-придирка, но ранний возврат является — условный вызов ловушки.

Evert 13.08.2021 21:32

@Evert Я не уверен, к чему ты клонишь. Возврат (ранний или другой) не является вызовом ловушки. Это может привести к условному вызову ловушки, если вызов ловушки происходит после раннего возврата, но в моем втором примере после раннего возврата вызова ловушки нет — обе ветки просто возвращают элементы React.

Ryan Cogswell 13.08.2021 21:40

Извините, это придирка к вашему комментарию: React can't tell the difference between an early return and a conditional hook call. Если перед вызовом ловушки был возврат, этот вызов ловушки является условный.

Evert 13.08.2021 21:51

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

import React from "react";
import ReactDOM from "react-dom";

const HooksHost = ({ children }) => children();

function App() {
  const [state1, setState1] = React.useState(1);

  if (state1 === 3) {
    return <div>State 1 is 3</div>;
  }

  return (
    <HooksHost>
      {/* should be named to pass lint rule that checks if it starts from 'use' then it is custom hook */}
      {function useHooks() {
        const [state2, setState2] = React.useState(2);
        return (
          <div className = "App">
            <div>State 1: {state1}</div>
            <div>State 2: {state2}</div>
            <button onClick = {() => setState1(state1 + 1)}>
              Increment State 1
            </button>
            <button onClick = {() => setState2(state2 + 1)}>
              Increment State 2
            </button>
          </div>
        );
      }}
    </HooksHost>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

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