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

Мы используем React для создания переиспользуемых компонентов, которые могут логично использоваться для создания UI. Создание компонентов в React также просто, как и создание функций ???? .

Для примера ????????, ниже показан простой react компонент, в который мы отправляем данные как аргумент, который можно легко указать внутри функции.

function Component(props){
	return <h1>{props.text}</h1>
}

Окей, начнем с того, что такое состояние в React?

Состояние – это место, в котором хранятся данные, предназначенные для компонента. Когда состояние меняется, компонент начинает перерисовываться, что позволяет нам управлять измененными данными в приложении.

Теперь давайте узнаем о состоянии при использовании useState().


useState()

const component = () => {
	// Давайте вызовем console.log, передадим в него useState 
	// и посмотрим, что он вернет
	console.log(useState(100));
	// Он возвращает массив [100, функция]
	// Возвращается состояние, а также функция для обновления состояния
	// Мы можем деструктуризировать массив 
	// для получения значения состояния и функции
	const [state, setState] = useState(100);


	return (
		<div>
			Привет!!!
		</div>
	)
}

Совет: используйте состояние только внутри компонентов.

Но вы не можете обновлять значение состояния используя оператор “=”, т.к. это хоть и изменит значение, но не запустит перерисовку компонента. React хочет, чтобы вы передавали значение через функцию setState, для обновления состояния.

Передача функции внутрь useState()

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

const [state, setState] = useState(() => {
	console.log("initial state");
	return 100;
});

Передача функции внутри setState()

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

onClick={() => {
	// Аргумент value это текущее состояние 
	setState((value) => {
		return value + 1;
	});
}} 

useEffect()

useEffect хук имеет 2 части, первая – это функция, и вторая – это массив зависимости, что является опциональным.

useEffect(()=>{},[])

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

useEffect(() => {
   console.log('change');
})

Примечание: первый вызов useEffect() будет сразу после того, как ваш компонент будет вмонтирован в DOM.

Массив зависимостей

Мы можем указывать состояние внутри массива зависимости у useEffect, чтобы он отслеживал только те изменения состояний, которые были указаны внутри массива.

const [state1, setState1] = useState(0);
const [state2, setState2] = useState(0);
useEffect(() => {
	console.log('state1 changed');
}, [state1])

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

Функция очистки

Внутри useEffect всегда можно вернуть функцию очистки, которая используется для удаления нежелательного поведения. Функция очистки вызывается не только перед размонтированием компонента, но и перед выполнением следующего эффекта, прочтите для деталей (на английском).

useEffect(() => {
	console.log(`state1 changed | ${state1}`);
	return () => {
		console.log('state1 unmounted | ', state1);
	}
}, [state1])

Вы можете получать данные из api как в примере ????????.

useEffect(() => {
	const url = "https://jsonplaceholder.typicode.com/todos/1";
	const fetchData = () => {
		fetch(url)
			.then(res => res.json())
			.then(data => {
				setState(data.title)
			})
	}
		fetchData();
	}, []);

useContext()

Контекст api позволяет передавать данные в дочерние компоненты без указывания их в props.

import { createContext } from "react";
import { useState } from "react";

const StoreContext = createContext();

const component = () => {
    const data = useState({
        name: 'Ritesh',
        email: 'someMail@gmail.com',
    })[0];

    const Child = () => {
        return <div>
            <StoreContext.Consumer>
                {value => <h1>Ваше имя: {value.name}</h1>}
            </StoreContext.Consumer>
        </div>
    }

    return (
        <StoreContext.Provider value={data}>
            <Child />
        </StoreContext.Provider>
    )
}

export default component;

Вы можете обернуть родительский компонент в Context.Provider и использовать его внутри функции Context.Consumer. Что же делает useContext? Он заменяет Context.Consumer и позволяет нам получать данные используя useContext.

Посмотрите на пример ????????.

import { createContext, useContext } from "react";
import { useState } from "react";

const StoreContext = createContext();

const component = () => {
    const data = useState({
        name: 'Ritesh',
        email: 'someMail@gmail.com',
    })[0];

    const Child = () => {
        const value = useContext(StoreContext);
        return <div>
            <h1>Ваше имя: {value.name}</h1>
        </div>
    }

    return (
        <StoreContext.Provider value={data}>
            <Child />
        </StoreContext.Provider>
    )
}

export default component;

Подробнее.


useReducer()

useReducer используется для управления состоянием в React, это, отчасти, схоже с функцией reduce в javascript.

// функция редюсера принимает в себя 2 параметра
// текущее состояние и экшен, и возвращает новое состояние
reducer(currentState, action)
// useReducer принимает в себя также 2 параметра
// функцию редюсера и изначальное состояние
useReducer(reducer, initialState);

Давайте создаем простой счетчик используя useReducer

import { useReducer } from 'react'

const initialState = 0;
const reducer = (state, action) => {
    switch (action) {
        case 'increment':
            return state + 1;
        case 'decrement':
            return state - 1;
        default:
            return state;
    }
}

export default function main() {
    const [count, dispatch] = useReducer(reducer, initialState);

    return (
        <div>
            <p>Значение: {count}</p>
            <button onClick={() => dispatch('increment')}>+</button>
            <button onClick={() => dispatch('decrement')}>-</button>
        </div>
    )
}

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

import { useReducer } from 'react'

const initialState = {
    firstCounter: 0,
    secondCounter: 0
};
const reducer = (state, action) => {
    switch (action.type) {
        case 'increment':
            return { ...state, firstCounter: state.firstCounter + action.value };
        case 'decrement':
            return { ...state, firstCounter: state.firstCounter - action.value };
        default:
            return { ...state };
    }
}

export default function main() {
    const [count, dispatch] = useReducer(reducer, initialState);

    return (
        <div>
            <p>Значение: {count.firstCounter}</p>
            <button className='bg-gray-200 p-2' onClick={() => dispatch({ type: 'increment', value: 2 })}>
                Увеличить на 2
            </button>
            <button className='bg-gray-200 p-2' onClick={() => dispatch({ type: 'decrement', value: 4 })}>
                Увеличить на 4
            </button>
        </div>
    )
}

Или, мы можем использовать несколько useReducer.

import { useReducer } from 'react'

const initialState = 0;
const reducer = (state, action) => {
    switch (action) {
        case 'increment':
            return state + 1;
        case 'decrement':
            return state - 1;
        default:
            return state;
    }
}

export default function main() {
    const [count, dispatch] = useReducer(reducer, initialState);
    const [count2, dispatch2] = useReducer(reducer, initialState);

    return (
        <div>
            <p>Счетчик: {count}</p>
            <button className="bg-gray-100 p-2 m-2"
                onClick={() => dispatch('decrement')}>-</button>
            <button className="bg-gray-100 p-2 m-2"
                onClick={() => dispatch('increment')}>+</button>

            <p>Счетчик 2: {count2}</p>
            <button className="bg-gray-100 p-2 m-2"
                onClick={() => dispatch2('increment')}>+</button>
            <button className="bg-gray-100 p-2 m-2"
                onClick={() => dispatch2('decrement')}>-</button>
        </div>
    )
}

Когда же использовать useState, а когда useReducer???

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


useReducer() вместе с useContext()

Используя вместе useContext и useReducer мы можем управлять глобальным состояние на любом уровне в дереве компонентов, давайте взглянем на пример ????????.

// main.jsx
import React from 'react'
import { useReducer } from 'react'
import ChildrenA from '../components/ChildrenA';

export const StateContext = React.createContext();
const initialState = 0;
const reducer = (state, action) => {
    switch (action) {
        case 'increment':
            return state + 1;
        case 'decrement':
            return state - 1;
        default:
            return state;
    }
}

export default function main() {
    const [count, dispatch] = useReducer(reducer, initialState);
    return (
        <div>
            <StateContext.Provider
                value={{ countState: count, countDispatch: dispatch }}>
                <ChildrenA />
            </StateContext.Provider>
        </div >
    )
}

// ChildrenA.jsx

import React from 'react'
import ChildrenB from './ChildrenB'
import { StateContext } from '../pages/main'
import { useContext } from 'react'

export default function ChildrenA() {
    const { countState, countDispatch } = useContext(StateContext)
    return (
        <div>
            У child A значение счетчика равно {countState}
            <ChildrenB />
        </div>
    )
}

// ChildrenB.jsx

import React from 'react'
import { StateContext } from '../pages/main'
import { useContext } from 'react'

export default function ChildrenB() {
    const { countState, countDispatch } = useContext(StateContext)
    return (
        <div>
            <p>Счетчик равен {countState}</p>
            <button onClick={() => countDispatch('increment')}>+</button>
            <button onClick={() => countDispatch('decrement')}>-</button>
        </div>
    )
}

Оба состояние буду изменены одновременно.


useCallback()

Давайте посмотри на код и попытаемся понять поведение функций в React.

import React from 'react'

export default function main() {

    function Sum() {
        return (a, b) => a + b;
    }
    const func1 = Sum();
    const func2 = Sum();
    console.log(func1 === func2);

    return (
        <div>main</div>
    )
}

Когда мы запустим код мы увидим “false” в логах.

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

// main.jsx
import React, { useState } from 'react'
import ChildrenA from '../components/ChildrenA';
import ChildrenB from '../components/ChildrenB';
import ChildrenC from '../components/ChildrenC';

const main = () => {
    const [state1, setState1] = useState(0);
    const [state2, setState2] = useState(0);

    const handleClickA = () => {
        setState1(state1 + 1);
    }

    const handleClickB = () => {
        setState2(state2 + 1);
    }

    return (
        <div className='flex flex-col justify-center items-center'>
            <ChildrenA value={state1} handleClick={handleClickA} />
            <ChildrenB value={state2} handleClick={handleClickB} />
            <ChildrenC />
        </div>
    )
}

// React.memo позволяет перерисовывать компонет только тогда,
// когда изменяются props
export default React.memo(main);

// ChildrenA.jsx
import React from 'react'

function ChildrenA({ value, handleClick }) {
    console.log('ChildrenA');
    return (
        <div>ChildrenA  {value}
            <button className='bg-gray-200 p-2 m-2' onClick={handleClick} >Click</button>
        </div>

    )
}

export default React.memo(ChildrenA);

// ChildrenB.jsx
import React from 'react'

function ChildrenB({ value, handleClick }) {
    console.log('ChildrenB');
    return (
        <div>ChildrenB {value}
            <button className='bg-gray-200 p-2 m-2' onClick={handleClick} >Click</button>
        </div>
    )
}

export default React.memo(ChildrenB);

// ChildrenC.jsx

import React from 'react'

function ChildrenC() {
    console.log('ChildrenC');
    return (
        <div>ChildrenC</div>
    )
}

export default React.memo(ChildrenC);

Когда вы посмотрите на console.log в браузере, вы увидите, что сразу происходит отрисовка всех трёх компонентов, но по нажатию на любую кнопку только 2 компонента перерисуются.

Примечание: здесь мы используем React.memo(), вот почему ChildrenC не перерисовывается, т.к. props не изменяются. Но почему же при изменении ChildrenA ChildrenB также перерисовывается?

Причина в том, что при перерисовке родительского компонента функция handleClick не таже самая. Я объяснял это в пункте выше, описывая как React определяет изменения в props. Вот почему перерисовываются сразу ChildrenA и ChildrenB.

Для решения этой проблемы мы будем использовать useCallback.

useCallback возвращает memoized callback (мемоизированную функцию, функцию, которая измениться только тогда, когда один из пунктов внутри массива зависимостей изменится).
useCallback принимает в себя функцию и массив зависимостей, также как и useEffect.

Давайте изменим наш родительский компонет, и посмотрим на логи.

// main.jsx

import React, { useState, useCallback } from 'react'
import ChildrenA from '../components/ChildrenA';
import ChildrenB from '../components/ChildrenB';
import ChildrenC from '../components/ChildrenC';

const main = () => {
    const [state1, setState1] = useState(0);
    const [state2, setState2] = useState(0);


    const handleClickA = useCallback(() => {
        setState1(state1 + 1);
    }, [state1])

    const handleClickB = useCallback(() => {
        setState2(state2 + 1);
    }, [state2])

    return (
        <div className='flex flex-col justify-center items-center'>
            <ChildrenA value={state1} handleClick={handleClickA} />
            <ChildrenB value={state2} handleClick={handleClickB} />
            <ChildrenC />
        </div>
    )
}

export default React.memo(main);

Теперь вы можете убедиться, что “все в шоколаде” ????????.


useMemo()

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

import React, { useState, useMemo } from 'react'

function factorialOf(n) {
    console.log('factorialOf(n) called!');
    return n <= 0 ? 1 : n * factorialOf(n - 1);
}

const main = () => {
    const [number, setNumber] = useState(2)
    const factorial = useMemo(() => factorialOf(number), [number])
    const [count, setCount] = useState(0)

    return (
        <div className='flex flex-col justify-center items-center'>
            {factorial}
            <button className='bg-gray-200 p-2 m-2' onClick={() => setNumber(number + 1)}>+</button>
            {count} <button className='bg-gray-200 p-2 m-2' onClick={() => setCount(count + 1)}>+</button>
        </div>
    )
}

export default main;

useRef()

Давайте выведем useRef в консоль, и посмотрим, что он возвращает.

console.log(useRef(100))
// выведится что-то типа ???????? {current: 100}

useRef возвращает изменяемый ref объект, в котором значение “current” устанавливается переданным аргументом при инициализации (initialValue). Возвращенный объект будет сохранен на протяжении всей жизни компонента.

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

import { useEffect, useState, useRef } from "react";

const component = () => {
    const obj1 = { hi: 100 };
    const obj2 = useRef({ hi: 100 });
    console.log(obj1 === obj2.current);

    const [state, setState] = useState(() => {
        return 1;
    });

    useEffect(() => {
        console.log('obj1 changed | ', obj1);
    }, [obj1])

    useEffect(() => {
        console.log('obj2 changed | ', obj2.current);
    }, [obj2])


    return (
        <div onClick={() => {
            setState((value) => {
                return value + 1;
            });
        }} className="w-screen h-screen flex justify-center items-center text-4xl font-extralight">
            {state}
        </div>
    )
}

export default component;

???????????????? Поздравляю!!!
Мы охватили все важные хуки. Спасибо за прочтение!

Комментарии (28)


  1. markelov69
    13.04.2022 10:32
    -5

    А почему сразу не дать людям которые начинают писать на реакте дружеский совет?
    Использовать сразу MobX для управления любым состоянием в приложении, как глобальном так и локальном. Вместо того, чтобы зарывать себя и писать лютый говнокод, который не избежен когда используешь useState и useReducer.


    1. sovaz1997
      13.04.2022 11:47
      +1

      Если не заниматься ерундой по типу добавления мешанины из простейших хуков без разделения на составные четко выраженные логические части в один компонент, оборачивать все подряд в useCallback и думать, что вы делаете производительный код, добавлять новый state там, где его нет (либо он напрямую зависит от другого state-а), передавать 100500 пропсов через 10 уровней вложенности, не зная, что такое контекст, заниматься копипастом и раздувать компоненты на 200 строк и более строк... Тогда да, это будет лютый говнокод. Да, я тоже раньше предпочитал MobX, но со временем смог оценить подход хуков, когда научился все разносить. Винить инструмент не надо, надо просто уметь им пользоваться. Вот вы умеете пользоваться MobX - и прекрасно, пользуйтесь! Я лично для себя сейчас больше предпочитаю хуки и пока не вижу кейсов использования MobX для себя. Хуки прекрасно декомпозируются, как и обычные функции: есть входные параметры, есть выходные, а дальше комбинируй как как нужно. Прекрасно отделяется view от логики, если это использовать правильно.

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


      1. markelov69
        13.04.2022 11:55
        -1

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


        1. sovaz1997
          13.04.2022 12:08
          +1

          Приведите тогда пример, где хуки не подойдут. Какой смысл говорить, когда можно это доказать? В говнокод можно превратить все при недостатке квалификации, либо времени. Пока я вижу, что вы якоритесь (когнитивное искажение) на этом, как-то давно поняв/увидев по каким-то примерам, что хуки = говнокод.

          Ну а проект - я не знаю, можно ли это называть Hello world-ом этот пет-проект (https://www.youtube.com/watch?v=6ia1rkQ--sU) + мобильное приложение с одновременной записью нескольких треков с бекендом тоже мои. Есть трансляция в реалтайм. Писалось потому, что было нужно в личных целях и альтернативы не нашел.


          1. markelov69
            13.04.2022 12:11

            Так покажите тогда ваш не говнокод с хуками.


            1. sovaz1997
              13.04.2022 12:19

              Весь код показать не смогу, т. к. не собирался делать его открытым, но некоторые полезные хуки, которые могут оказаться полезными, смогу показать, конечно. Сегодня, либо на неделе смогу что-то показать, думаю.


              1. markelov69
                13.04.2022 16:16

                Ждёмс


                1. sovaz1997
                  15.04.2022 13:30
                  -1

                  Некоторые хуки, которые используются в проекте: https://learned-dungeon-cb7.notion.site/ddf1044de9e940b5b732926736b7b2f6


                  Код не раздувается таким образом, всегда есть возможность улучшить еще, с жизненным цикла реакте проблем нет, с производительностью проблем нет, подключаются хуки удобно, unmount с отписками также очень удобный. Лично для себя проблем не вижу, хотя раньше я тоже предпочитал MobX, когда не умел работать с хуками.


                  1. mayorovp
                    15.04.2022 13:47

                    Что-то хуки с той страницы вообще не относятся к управлению состоянием. Какое отношение они имеют к обсуждению mobx vs useState?


                    1. sovaz1997
                      15.04.2022 13:56

                      Эмм, меня попросил человек скинуть пример своих хуков, я скинул, в чем проблема? Если мы говорим об управлении состоянием, то что именно вы под этим подразумеваете? Нужен конкретный пример задачи. Состояние - слишком абстрактное понятие, useState/MobX - слишком конкретное. Я не вам отвечал, как вы могли заметить. "Ничего не понятно, но минус поставлю" называется


                      1. markelov69
                        15.04.2022 14:27

                        5 страниц с формами по 20 полей допустим, у каждого поля есть валидация, отправка на бэк данных из этих полей. Всевозможные флаги типо показать скрыть какой-то блок. Вот в таком роде банальные задачи фронта как вы решаете. А то что вы скинули выше как заметил mayorovp не имеют отношения к управлению состоянием приложения и вообще никакого отношения не имеет к контексту useState vs MobX


                      1. sovaz1997
                        15.04.2022 14:45

                        С большими формами работал только на Vue (но там полей могло и больше 100 быть).

                        Для начала, я бы не стал писать велосипед: https://react-hook-form.com/

                        2-е. Если бы скрытие блоков начало превращаться в говнокод и скрывать надо было бы много всего + поля, которые скрыты, могли бы либо отправляться, либо не отправляться на бекенд, то придумал бы решение. Если бы была форма больше, подумал бы над разделением на блоки и над тем и думал бы, как лучше провайдить форму. Хуки рефакторить кто-то запрещает?

                        Все зависит от задачи и для разных проектов подойдут разные решения с разным уровнем абстракции той или иной части.


                      1. markelov69
                        15.04.2022 17:28
                        -1

                        Т.е. вы никогда не имели реального опыта и практики писать самые распространенные задачи в различных вэб приложений на ректе, зато утверждаете что хуки(имеем ввиду управления состоянием с использованием useState) это круто, более того как минимум ни чем не хуже чем MobX, ещё и якобы вы раньше предпочитали MobX, но в реальности на реакте у вас и опыта получается что нет. Т.е. вы в своих фантазиях уже нафантазировали как все устроено, а к практике так и не перешли, но зато с удовольствием транслируете свои фантазии.


                        Огорчу, когда вы на практике начнете писать реальное вэб приложение, прямо типовое, и буду использовать для управления состояние реакт, т.е. useState вы просто ахренеете от жизни, насколько все получается громоздко, неудобно и вообще на все это без крови из глаз не взглянешь. Боже упаси если в этом коде обнаружится баг или нужно будет внести изменения в бизнес логику. Поэтому любой опытный разработчик который давно с React'ом использует его только в связка с MobX и никак иначе. Извращенцев, мазохистов и тех кто в танке в расчет не берем.


                        То, что вы взяли рандомную библиотеку в качестве react-hook-form(опять же огорчу, это такая-же полная хрень как и Formik и множество ему подобных, которая вставляет много палок в колеса и заставляет вокруг себя лепить тонны костылей) и якобы тем самым считаете что поступили правильно и не изобретаете велосипед, при чем в реальности в с ней даже ни разу не работали. Вообще все это говорит лишь о том, что вы только только вступили на путь разработчика, но уже ошибочно считаете себя опытным и прожженым.


                      1. sovaz1997
                        15.04.2022 17:48

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

                        Перехода на личности от меня не ждите, т. к. я прекрасно понимаю, что квадратик с ником рядом и комментарием ничего не говорит о личности.

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


    1. mayorovp
      13.04.2022 15:29
      +1

      Вы сейчас тёплое с мягким сравнили. MobX лучше подходит для глобального состояния, useState и useReducer — для локального.


      1. markelov69
        13.04.2022 15:58
        -1

        useState и useReducer — для локального.
        Нет. Для локального MobX все равно гораздо лучше.


        1. mayorovp
          13.04.2022 17:21

          Чем оно лучше-то?


          Рассмотрим простейший пример (если я ничего не перепутал):


          function Foo() {
              const [value, setValue] = useState();
              return <>
                  <div>{value}</div>
                  <input onchange={e => setState(e.target.value)} />
              </>;
          }

          Куда вы сюда MobX засунете и зачем?


          1. markelov69
            13.04.2022 17:36

            Вы привели синтетический пример из Hello World. В реальной жизни все далеко не так просто и такой банальщины нет.
            Вот уже реальная жизнь:


            function Foo() {
                const [state] = useState(() => new FooState());
                return <>
                    <div>Имя:{state.formFields.firstName.value}</div>
                    <Input formField={state.formFields.firstName} title="Имя" />
                    <Input formField={state.formFields.lastName} title="Фамилия" />
                    <Input formField={state.formFields.zip} title="Индекс" />
                    <Input formField={state.formFields.city} title="Город" />
                    <Button onClick={state.submit}>Сохранить</Button>
                </>;
            }

            Где FooState


            export class FooState {
                formFields = {
                    firstName: new FormField(''),
                    lastName: new FormField(''),
                    zip: new FormField(''),
                    city: new FormField(''),
                }
            
                submit = async () => {
                    // ..
                    validateFormFields(this.formFields);
                    const formData = getFormFieldsData(this.formFields);
                    // ..
                }
            }

            И этой логики обычно в рамках одного комонента на примере формы гораздо больше, с описанием валидациий и тд и тп. Описывать всё это используя хуки, это адская лапша и говнокод. Про то, что при каждом рендере все будет создаваться по новой, а не просто будут неизменные ссылки на объекы это вообще смешно и нелепо. Про заворачивание всего в useCallback/useMemo это так же смешно и нелепо писать лишний говнокод из-за того, что сам себя сознательно загнал в ловушку.


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


            1. mayorovp
              13.04.2022 17:50
              +1

              Я вижу у вас не локальное состояние, а разделяемое, как минимум между двумя компонентами (Foo и Input), а реально тут наверняка будет их гораздо больше.


              Кстати, обратите внимание, что вы тоже не смогли полностью отказаться от useState


              1. markelov69
                13.04.2022 18:01
                -1

                Я вижу у вас не локальное состояние, а разделяемое, как минимум между двумя компонентами (Foo и Input), а реально тут наверняка будет их гораздо больше.

                FooState это локальное состояние компонента Foo.


                Кстати, обратите внимание, что вы тоже не смогли полностью отказаться от useState

                Другого аналога constuctor'a у функционального компонента нет. Поэтому useState используется чисто для того чтобы создать и получить ссылку на экзмепляр класса который является локальным состоянием текущего компонента, который не будет пересоздаваться при каждом рендере.


            1. sovaz1997
              13.04.2022 18:44

              Попробуйте написать эту форму на хуках и мы посмотрим, какая там будет лапша и вместе ее съедим :)


  1. KhodeN
    13.04.2022 14:37

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


  1. DDroll
    13.04.2022 19:54

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


    1. AZverg
      13.04.2022 23:04

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

      Так что - больше статей хороших и разных :)


  1. kreddos
    13.04.2022 20:40
    +2

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


    1. sovaz1997
      13.04.2022 21:55

      Теорема о MobX:

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


    1. mayorovp
      14.04.2022 11:00

      Почему вы называете MaZaAa/markelov69 во множественном числе? Он же один такой на Хабре.


      1. kreddos
        14.04.2022 12:27

        Действительно, не обращал внимания, думал что разные люди пишут