В Jupiter мы создаем новые инструменты для фитнес-тренеров, чтобы упростить управление их клиентами. Мы работаем над выпуском нашего приложения, и оно скоро выйдет! Недавно мы создали функцию для создания новых тренировок с несколькими вложенными компонентами, в которых нам нужно было бы динамически добавлять или удалять компоненты, а также принимать данные от пользователя. После того, как мы запишем форму, нам нужно будет опубликовать ее в нашей серверной части, а это значит, что нам нужно будет иметь единое представление всех данных.

Проблема

Для начала у нас был объект тренировки, который является компонентом верхнего уровня. Нам нужно было создать форму, в которой пользователи могли бы динамически добавлять или удалять компоненты, а также при необходимости обновлять свои поля. Теперь мы могли бы сделать это с помощью хуков, но нам нужно было бы сохранить состояние в родительском объекте, а затем передать его на два уровня ниже, чтобы иметь возможность сохранять все ответы в компоненте Workout.

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

Решение

Введите hookstate. Что такое крючковое состояние? Что ж, это не API хуков, созданный React. Может быть, поэтому вы не видите эту библиотеку на первых двух страницах в Google. Но это действительно простая и легкая библиотека, и она может быть очень полезной для определенных случаев использования. Хотя мне лично нравятся хуки, они не идеальны для вложенных или глубоко вложенных компонентов.

Вот код, который оказался очень простым решением.

//WORKOUT.JS
import { useState } from “@hookstate/core”;
//The initial state of the workout component, which creates an //exercise with a name and it has a description. This is an array //which contains components of exercises. Each exercise component //creates an array of set objects.
export default function Workout() {
const state = useState([
{
name: “”,
desc:””,
sets: [{ reps: 10, weight: 0 },],
},]);
//We also create a new Exercise component every time the user clicks //a button. By default, each set has 12 reps and 0 additional //weight.
const createExercise = () => {
state.merge([{
name: “”,
sets: [{ reps: 12, weight: 0 }],},]);};
//Finally we map the exercise component and pass the exercise object //as props, I'll explain the benefits of passing the object in a //second.
return(
{state.map((exercise, index) => {
return <Exercise key={index} exercise={exercise}></Exercise>;
})}
<Button title=”Add Exercise” onPress={() => createExercise()} />
    );
}

Это базовый React, и на самом деле мы не делаем ничего другого, за исключением того факта, что мы используем библиотеку Hookstate.

Вот где использование Hookstate действительно принесло дивиденды! Концепция ограниченного состояния действительно упростила кодовую базу. Короче говоря, он преобразует реквизиты упражнений в записываемый объект (во всяком случае, я так об этом думаю!) С помощью всего одной строчки кода.

import { useState, none } from "@hookstate/core";
export default function Exercise({ exercise }) {
const exerciseState = useState(exercise);
//An exercise can have multiple sets and we can easily create this //with the merge function, which adds new Set objects to the end of //the array.
const handleAddSets = () => {
exerciseState.nested(“sets”).merge([{ reps: 10, weight: 0 }]);
console.log(“add sets”);
};
//We also needed to delete an Exercise component. The user needs to //be able to interact with the component. Here we can use //hookstate's set() function and delete the item by using none //object.
const handleDeleteExercise = () => {
exerciseState.set(none);
};
//In here, we pass the exercise name value and also track it when //the text changes.
return(
<TextInput
style={styles.exerciseDescription}
placeholder="Exercise name"
value={exerciseState.name.value}
onChangeText={(e) => exerciseState.name.set(e)}
/>
{exerciseState.sets.map((sets, index) => (
<Set key={index} sets={sets} index={index + 1} />))}
);}

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

export default function Set({ sets, index }) {
const setsData = useState(sets);
//Ability to delete sets
const handleDeleteSets = () => {
setsData.set(none);
};

По сути, hookstate упростил управление состоянием различных компонентов. У нас есть 3 компонента, и мы можем динамически

  • Захват пользовательского ввода
  • Добавить новые компоненты
  • Удалить текущие компоненты

Мы можем сделать это всего с помощью 20 строк кода.

Здесь нет шаблонного кода, он выглядит в стиле React и легко читается, что он делает.

Я лично изо всех сил пытался понять, как работает API, поскольку документации по библиотеке не так много, как для других, поэтому я подумал, что поделюсь своим опытом, если он поможет кому-то еще. Вот окончательная диаграмма с окончательным решением. В дополнение к этому есть много кода, который я здесь не добавил, но я хотел, чтобы он оставался простым и подчеркивал, насколько интересен hookState для сообщества! Вот последняя диаграмма компонентов с функциями.