Можно ли использовать React Hooks вне функционального компонента, или мне нужно использовать mobx или redux? - PullRequest
0 голосов
/ 29 мая 2020

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

Проблема в том, что, похоже, есть одно ограничение для React Hooks, их можно использовать только внутри функциональных составные части. В качестве примера возьмем сервер-клиент, которому необходимо изменить состояние isAuthenticated при получении 401.

//client.js
import { useUserDispatch, signOut } from "auth";

export function request(url, args) {
  var dispatch = useUserDispatch();
  return fetch(url, args).then(response => {
  if (response.status === 401) {
    logout(dispatch);
  }
}
);

//auth.js
import React from "react";

var UserStateContext = React.createContext();
var UserDispatchContext = React.createContext();

function userReducer(state, action) {
  ...
}

function UserProvider({ children }) {
  var [state, dispatch] = React.useReducer(userReducer, {
    isAuthenticated: false,
  });

  return (
    <UserStateContext.Provider value={state}>
      <UserDispatchContext.Provider value={dispatch}>
        {children}
      </UserDispatchContext.Provider>
    </UserStateContext.Provider>
  );
}

function useUserState() {
  return React.useContext(UserStateContext);
}

function useUserDispatch() {
  return React.useContext(UserDispatchContext);
}

function signOut(dispatch) {
  dispatch({});
}

export { UserProvider, useUserState, useUserDispatch, loginUser, signOut };

Приведенный выше код клиента вызовет ошибку «Хуки могут быть вызваны только внутри тела функциональная составляющая ». Так что, возможно, мне нужно переместить строку var dispatch = useUserDispatch() вверх в компонент, где вызывается request, и передать dispatch в качестве реквизита request. Я считаю, что это неправильно, не только request вынужден заботиться о каком-то бессмысленном (для него) dispatch, но также этот dispatch будет распространяться везде, где требуется компонент request.

Для компонентов на основе классов this.state тоже не решает эту проблему, но, по крайней мере, я могу использовать mobx.

Так есть ли другие идеальные способы решения этой проблемы?

Ответы [ 4 ]

1 голос
/ 29 мая 2020

Да, вы должны использовать Redux или MobX для решения этой проблемы. Вы должны поддерживать состояние isAuthenticated в глобальном состоянии Redux или MobX. Затем выполните действие, которое можно назвать, например, toggleAuthState, и передать его дочернему компоненту и переключить состояние оттуда.

Также вы можете использовать функциональные компоненты для этого случая. Компоненты на основе классов не являются обязательными для использования MobX или Redux. Если вы поддерживаете HO C как Container, вы можете передавать действия и состояния дочернему элементу.

Я показываю пример использования контейнера в качестве HO C:

// Container
import React from "react"
import * as actions from "../actions" 
import ChildComponent from "../components/ChildComponent"
import { connect } from "react-redux"
import { bindActionCreators } from "redux"

const Container = props => <ChildComponent { ...props } />

const mapStateToProps = state => ({ ...state })

const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(Container)

Затем в ChildComponent вы можете использовать свои состояния и действия отправки, когда вам нужно.

0 голосов
/ 02 июня 2020

Mobx и хуки очень похожи по реализации. Оба используют контекст рендеринга, который является в некотором смысле «глобальным». React связывает контекст рендеринга с контекстом рендеринга компонента, но Mobx сохраняет этот контекст рендеринга отдельно. Следовательно, это означает, что хуки должны быть созданы в пределах жизненного цикла рендеринга компонента (но иногда их можно вызывать вне этого контекста). Mobx-react связывает жизненный цикл рендеринга Mobs с жизненным циклом реакции, вызывая повторный рендеринг реакции при изменении наблюдаемых объектов. Таким образом, Mobx-react вкладывает контекст рендеринга реакции в контекст рендеринга Mobx.

React внутренне отслеживает хуки по количеству раз и порядку их вызова в цикле рендеринга компонента. Mobx, с другой стороны, оборачивает наблюдаемый объект с помощью прокси, который позволяет контексту мобов знать, были ли ссылки на какие-либо из его свойств во время «контекста рендеринга» Mobx (по сути, вызова autorun). Затем, когда свойство изменяется, Mobx знает, какие «контексты рендеринга» заботятся об этом свойстве, и повторно запускает эти контексты. Это означает, что везде, где у вас есть доступ к наблюдаемому объекту, вы можете изменить свойство на нем, и Mobx отреагирует на него.

Для обработчиков состояния реакции response предоставляет настраиваемую функцию установки для объекта состояния. Затем React использует вызовы этого сеттера, чтобы узнать, когда ему нужно повторно отрисовать компонент. Этот сеттер можно использовать где угодно, даже за пределами рендеринга React, но вы можете создать этот хук внутри вызова рендеринга, потому что в противном случае у реакции не будет способа сказать, какой компонент t ie этот крючок для. Создание ловушки неявно связывает ее с текущим контекстом рендеринга, и поэтому хуки должны быть созданы внутри вызовов рендеринга: они не имеют значения вне вызова рендеринга, потому что они не привязаны к компоненту. Но если они привязаны к компоненту, они должны быть доступны где угодно. Фактически, такие действия, как onClick или fetch обратный вызов, не происходят в контексте рендеринга, даже если обратный вызов создан в этом контексте - обратный вызов действия происходит после того, как реакция завершает рендеринг.

0 голосов
/ 29 мая 2020

Когда вы создаете хуки, вы должны ссылаться на Правила хуков

Вы можете вызывать хуки только из функций реакции. Не вызывайте ловушки из обычных JavaScript функций. Вместо этого вы можете:

✅ Вызвать хуки из компонентов функции React. ✅ Вызов хуков из пользовательских хуков (узнайте о них на этой странице ).

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

Например, здесь я реорганизую функцию request как ловушку.

export function useRequest(url, args) {
  var userDispatch = useUserDispatch();
  const fetcher = React.useCallback(() => {
    return new Promise((resolve, reject) =>
      fetch(url, args)
        .then((response) => {
          if (response.status === 401) {
            logout();
            reject();
          }

          resolve(response);
        })
        .catch(reject)
    );
  }, [url, args]);

  return [fetcher, userDispatch];
}

, а затем использую его.

function App() {
  const [fetch, userDispatch] = useRequest("/url", {});
  React.useEffect(() => {
    fetch().then((response) => {
      userDispatch({ type: "USER_REQUEST", payload: response });
    });
  }, []);
  return <div>Hello world</div>;
}
0 голосов
/ 29 мая 2020

Хуки поставляются как альтернатива компонентам на основе классов, вы должны выбрать один для своего проекта и придерживаться его, не путать. есть некоторая мотивация для создания хуков, как это лучше указано в документации: мотивация хуков .

вы можете создавать функции хуков отдельно, но они предназначены для потребления по компонентам. это что-то вроде использования HO C (компонент высокого порядка) с компонентами на основе классов.

const myHook = () => {
  [foo, setFoo] = useState('john')
  // use effect for example if you need to run something at state updates
  useEffect(() => { 
    // do something on foo changes
  }, [foo])

  return [foo, setFoo] // returning state and setState you can use them by your component
}

теперь у вас есть многоразовый крючок, и вы можете использовать его в своих компонентах:

const myComponent = (props) => {
  [foo, setFoo] = myHook()

  const handleFoo = () => { 
    // some logic
    setFoo(someValue)
  }      
  return (
    <div>
      <span>{foo}<span>
      <button onClick={handleFoo}>click</button>
    </div>
  )
}

obs: в настоящее время вам следует избегать объявления переменных как var, для большинства выберите const, а если это переменная-значение (например, число), которая нуждается в обновлении, используйте let.

...