Рубрики
Без рубрики

Лучшие способы использования реактивных крюков для управления государством

Узнайте лучшие способы использовать React Cloots для управления государством. Помечено в React, JavaScript, Teamescript, обсудить.

Прочитайте оригинальную статью в CodeThat.today

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

Я рассмотрел некоторые варианты и методы, которые входят дикие вместе с некоторыми объяснениями и критикой. Вы можете найти некоторые из этих примеров в этом Github repo. .

Базовый пример

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

Допустим, у нас есть приложение TODO. У нас есть следующая упрощенная структура контейнеров и компонентов:

Во-первых, это Заголовок который содержит входную форму для тодолиста.

type HeaderProps = {
  addTodo?: (text: string) => void;
}

const Header = ({ addTodo }: HeaderProps ) => {
  const onSave = (text: string) => {
    if (text.length !== 0) {
      addTodo && addTodo(text);
    }
  };
  return (
    

todos

) }

Где:

type TodoTextInputProps = {
  text?: string
  editing?: boolean;
  placeholder?: string;
  onSave: (text: string) => void;
  newTodo: boolean;
}
type TodoTextInputState = {
  text: string;
}

export class TodoTextInput extends React.Component {
  state = {
    text: this.props.text || ''
  };

  handleSubmit = (e: React.KeyboardEvent) => {
    const text = e.currentTarget.value.trim();
    if (e.which === 13) { // Enter Key
      this.props.onSave(text);
      if (this.props.newTodo) {
        this.setState({ text: '' });
      }
    }
  };

  handleChange = (e: React.FormEvent) => {
    this.setState({ text: e.currentTarget.value });
  };

  handleBlur = (e: React.FormEvent) => {
    if (!this.props.newTodo) {
      this.props.onSave(e.currentTarget.value);
    }
  };

  render() {
    return (
      
    );
  }
}

Тогда у нас есть СВЯЗЬ где мы показываем TODOS:

type MainSectionProps = {
    todos: Todo[];
    deleteTodo: (id: number) => void;
    editTodo: (id: number, text: string) => void;
    toggleTodo: (id: number) => void;
  }

  const MainSection = ({
    todos,
    deleteTodo,
    editTodo,
    toggleTodo,
  }: MainSectionProps) => {
    return (
      
); }; type TodoListProps = MainSectionProps const TodoList = ({ todos, editTodo, deleteTodo, toggleTodo }: TodoListProps) => (
    {todos.map((todo: Todo) => ( ))}
);

Где:

type TodoItemProps = Pick & {
    todo: Todo;
  }
  type TodoItemPropsState = {
    editing: boolean;
  }

  export class TodoItem extends React.Component {
    state = {
      editing: false
    };

    handleDoubleClick = () => {
      this.setState({ editing: true });
    };

    handleSave = (id: number, text: string) => {
      if (text.length === 0) {
        this.props.deleteTodo(id);
      } else {
        this.props.editTodo(id, text);
      }
      this.setState({ editing: false });
    };

    render() {
      const { todo, toggleTodo, deleteTodo } = this.props;

      let element;
      if (this.state.editing) {
        element = (
           this.handleSave(todo.id, text)}
            newTodo={false}
          />
        );
      } else {
        element = (
          
toggleTodo(todo.id)} />
); } return (
  • {element}
  • ); } }

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

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

    Пользовательское состояние крюка

    Это самый простой способ. Мы предоставляем Пользовательский крючок Это будет предложить всю необходимую бизнес-логику, например, необходимые контейнера, например:

    type Todo = {
      id: number;
      completed: boolean;
      text: string;
    }
    
    const useTodos = () => {
      const [todos, setTodos] = useState([]);
    
      const addTodo = (text: string) => {
        setTodos([
          ...todos,
          {
            id: todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
            completed: false,
            text
          }
        ]);
      };
    
      const deleteTodo = (id: number) => {
        setTodos(todos.filter(todo => todo.id !== id));
      };
    
      const editTodo = (id: number, text: string) => {
        setTodos(todos.map(todo => (todo.id === id ? { ...todo, text } : todo)));
      };
    
      const toggleTodo = (id: number) => {
        setTodos(
          todos.map(
            todo =>
              todo.id === id ? { ...todo, completed: !todo.completed } : todo
          )
        );
      };
    
      return [
        todos,
        {
          addTodo,
          deleteTodo,
          editTodo,
          toggleTodo,
        }
      ];
    };
    
    const App = () => {
      const [
        todos,
        { addTodo, deleteTodo, editTodo, toggleTodo }
      ]: any = useTodos();
    
      return (
        
    ); };

    Здесь вся государственная логика управления инкапсулирована в многоразовом Usetodos крюк. Мы возвращаем список TODOS и список операций, которые поставляются с ними. Когда мы вызываем одну из функций модификации, в списке TODOS обновляется, и весь список воспроизводится, как показано в примере ниже:

    Вещи, которые мне нравятся: Простота. Нет центрального хранения или комплексного обеспечения. Мы просто беру его и используем его.

    Вещи, которые мне не нравятся: Простота. Он может не масштабироваться в больших приложениях или когда у нас есть сложный набор данных. Для небольших программ, которые имеют дело с небольшим выделенным доменом, это идеально.

    Пользовательские крючки + React Context

    Это сборки на вершине идей, объясненные в этом Статья Отказ Мы используем комбинацию пользовательского крючка для управления состоянием:

    import React from "react";
    import { useState, useMemo, useContext } from "react";
    import { Todo } from "../Example5";
    
    const AppContext = React.createContext({});
    
    /**
     * Our custom React hook to manage state
     */
    
    type AppState = {
      todos: Todo[];
    };
    
    const useAppState = () => {
      const initialState: AppState = { todos: [] };
      // Manage the state using React.useState()
      const [state, setState] = useState(initialState);
    
      // Build our actions. We'll use useMemo() as an optimization,
      // so this will only ever be called once.
      const actions = useMemo(() => getActions(setState), [setState]);
    
      return { state, actions };
    };
    
    // Define your actions as functions that call setState().
    // It's a bit like Redux's dispatch(), but as individual
    // functions.
    const getActions = (
      setState: React.Dispatch>
    ) => ({
      deleteTodo: (id: number) => {
        setState((prevState: AppState) => ({
          ...prevState,
          todos: prevState.todos.filter((todo: Todo) => todo.id !== id)
        }));
      },
      editTodo: (id: number, text: string) => {
        setState((prevState: AppState) => ({
          ...prevState,
          todos: prevState.todos.map((todo: Todo) =>
            todo.id === id ? { ...todo, text } : todo
          )
        }));
      },
      toggleTodo: (id: number) => {
        setState((prevState: AppState) => ({
          ...prevState,
          todos: prevState.todos.map((todo: Todo) =>
            todo.id === id ? { ...todo, completed: !todo.completed } : todo
          )
        }));
      },
      addTodo: (text: string) => {
        setState((prevState: AppState) => ({
          ...prevState,
          todos: [
            ...prevState.todos,
            {
              id:
                prevState.todos.reduce(
                  (maxId, todo) => Math.max(todo.id, maxId),
                  -1
                ) + 1,
              completed: false,
              text
            }
          ]
        }));
      }
    });
    
    // Sub-components can use this function. It will pick up the
    // `state` and `actions` given by useAppState() higher in the
    // component tree.
    const useAppContext = (): any => {
      return useContext(AppContext);
    };
    
    export { AppContext, useAppState, useAppContext };
    

    Тогда мы можем использовать это как:

    const TodoList: React.FC = () => {
      const { state, actions } = useAppContext();
    
      return (
        
    ); }; const App: React.FC = () => { const { state, actions } = useAppState(); return (
    ); }; export default App;

    В приведенном выше примере мы разделяем действия из штата, и мы используем глобальный AppContext в качестве провайдера для этих ценностей. Тогда любой компонент может вызвать iseapcontext. Чтобы получить этот контекст для использования.

    Вещи, которые мне нравятся: Разделение действий от состояния. Использование Реагировать. Контекст API как улучшение предыдущего примера.

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

    Redux + крючки + прокси

    Последний пример основан на вершине идей, объясненных в этом Статья Отказ Здесь мы держим наш старый старый магазин Redux со всеми нашими редукторами, начальным состоянием и т. Д.

    import { createStore } from 'redux';
    import { Todo } from './models';
    
    export type AppState = {
      todos: Todo[];
    };
    
    const reducer = (state = AppState, action: any) => {
      switch (action.type) {
        case 'ADD_TODO':
          return { ...state, todos: [
            ...state.todos,
            {
              id: state.todos.reduce((maxId: number, todo: Todo) => Math.max(todo.id, maxId), -1) + 1,
              completed: false,
              text: action.text
            }
          ] };
        case 'DELETE_TODO':
          return { ...state, todos: state.todos.filter((todo: Todo) => todo.id !== action.id) };
        case 'TOGGLE_TODO':
          return { ...state, todos: state.todos.map((todo: Todo) =>
            todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
          ) };
        case 'EDIT_TODO':
          return { ...state, todos: state.todos.map((todo: Todo) =>
            todo.id === action.id ? { ...todo, text: action.text } : todo
          ) };
        default:
          return state;
      }
    };
    
    const store = createStore(reducer);
    
    export default store;
    

    Тогда нам нужно создать следующую Mumbo Jumbo, которая выполняет всю логику:

    import React, { useContext, useEffect, useReducer, useRef, useMemo } from 'react';
    
    const ReduxStoreContext = React.createContext({});
    
    export const ReduxProvider = ({ store, children }: any) => (
      
        {children}
      
    );
    
    export const useReduxDispatch = () => {
      const store: any = useContext(ReduxStoreContext);
      return store.dispatch;
    };
    
    const forcedReducer = (state: any) => !state;
    const useForceUpdate = () => useReducer(forcedReducer, false)[1];
    
    export const useReduxState = () => { 
      const forceUpdate: any = useForceUpdate();
      const store: any = useContext(ReduxStoreContext);
      const state = useRef(store.getState());
      const used: any = useRef({});
      const handler = useMemo(() => ({
        get: (target: any, name: any) => {
          used.current[name] = true;
          return target[name];
        },
      }), []);
      useEffect(() => {
        const callback = () => {
          const nextState = store.getState();
          const changed = Object.keys(used.current)
            .find(key => state.current[key] !== nextState[key]);
          if (changed) {
            state.current = nextState;
            forceUpdate();
          }
        };
        const unsubscribe = store.subscribe(callback);
        return unsubscribe;
      }, []);
      return new Proxy(state.current, handler);
    };
    

    Подробное объяснение написано в учебной статье. Как только у нас есть эта логика, мы можем использовать это так:

    const App: React.FC = () => (
      
        
      
    );
    
    const TodoList: React.FC = () => {
      const state = useReduxState();
      const dispatch = useReduxDispatch();
    
      const addTodo = useCallback((text: string) => dispatch({ type: 'ADD_TODO', text: text, }), []);
      const deleteTodo = useCallback((id: number) => dispatch({ type: 'DELETE_TODO', id: id, }), []);
      const editTodo = useCallback((id: number, text: string) => 
        dispatch({ type: 'EDIT_TODO', id: id, text: text }), []);
      const toggleTodo = useCallback((id: number) => dispatch({ type: 'TOGGLE_TODO', id: id }), []);
    
      return (
        
    ); };

    Конечно, мы можем извлечь все действия отправки в отдельном месте, используйте селекторы и т. Д., Но большая часть функциональности аналогична.

    Вещи, которые я люблю: Он хорошо играет с существующими магазинами Redux, действиями и редукторами.

    Вещи, которые мне не нравятся: Эта мульбо Джумбо там выглядит странно. Мы не уверены, какие последствия с точки зрения производительности. Прокси не доступны в IE11.

    Текс Вот и это, я надеюсь, что эта статья демистила использование реактивных крюков для управления государством. В целом, я думаю, что реагированные крюки дополняют Redux с точки зрения пригодности для целей и удобства при обработке состояния. Из трех примеров я вроде как последнее, если оно, давайте удерживайте это магазин redux на месте.

    А что насчет тебя? Можете ли вы показать несколько примеров масштабируемого государственного управления с помощью реактивных крюков, которые вы можете поделиться, или вы думаете, что они лучше?

    Оригинал: “https://dev.to/theodesp/best-ways-to-use-react-hooks-for-state-management-44h3”