React.js. Функциональное программирование

03.09.2021

Теги: JavaScriptReact.jsWeb-разработкаКомпонентФункция

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

import { useState } from 'react';

export default function Calculator() {
    const [one, setOne] = useState(0);
    const [two, setTwo] = useState(0);

    const handleChangeOne = (event) => {
        const value = event.target.value;
        setOne(value ? parseFloat(value) : 0);
    }

    const handleChangeTwo = (event) => {
        const value = event.target.value;
        setTwo(value ? parseFloat(value) : 0);
    }
    
    return (
        <p>
            <input type="number" value={one} onChange={handleChangeOne} />
            <input type="number" value={two} onChange={handleChangeTwo} />
        </p>
    );
}
}

Здесь пока нет ничего сложного. Есть два input-поля для ввода чисел, над которыми мы планируем выполнять арифметические операции. Теперь нам нужны четыре кнопки, которые позволят переключаться с одной арифметической операции на другую.

import { useState } from 'react';

export default function Calculator() {
    const [one, setOne] = useState(0);
    const [two, setTwo] = useState(0);
    const [simbol, setSimbol] = useState('+');

    const handleClick = (simbol) => {
        setSimbol(simbol);
    }

    const handleChangeOne = (event) => {
        const value = event.target.value;
        setOne(value ? parseFloat(value) : 0);
    }

    const handleChangeTwo = (event) => {
        const value = event.target.value;
        setTwo(value ? parseFloat(value) : 0);
    }
    
    return (
        <>
            <p>
                <button onClick={() => handleClick('+')}>PLUS</button>
                <button onClick={() => handleClick('-')}>MINUS</button>
                <button onClick={() => handleClick('*')}>MULTIPLY</button>
                <button onClick={() => handleClick('/')}>DIVIDE</button>
            </p>
            <p>
                <input type="number" value={one} onChange={handleChangeOne} />
                <span> {simbol} </span>
                <input type="number" value={two} onChange={handleChangeTwo} />
            </p>
        </>
    );
}

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

import { useState } from 'react';

const plus = (a, b) => a + b;
const minus = (a, b) => a - b;
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;

export default function Calculator() {
    const [one, setOne] = useState(0);
    const [two, setTwo] = useState(0);
    const [simbol, setSimbol] = useState('+');
    // Функцию текущей арифметической операции храним в состоянии. Передаем useState не функцию plus, а
    // функцию () => plus, потому что useState может принимать функцию для вычисления начального state.
    // Если мы передаем useState строку или число — useState понимает, что это начальный state; а если
    // передаем функцию plus — useState считает, что надо вызвать plus для вычисления начального state.
    const [action, setAction] = useState(() => plus);

    const handleClick = (action, simbol) => {
        // передаем setState не функцию action, а функцию () => action, потому что setAction может принимать
        // в качестве аргумента функцию; эта функция принимает старый state и должна вернуть новый state
        setAction(() => action);
        setSimbol(simbol);
    }

    const handleChangeOne = (event) => {
        const value = event.target.value;
        setOne(value ? parseFloat(value) : 0);
    }

    const handleChangeTwo = (event) => {
        const value = event.target.value;
        setTwo(value ? parseFloat(value) : 0);
    }
    
    return (
        <>
            <p>
                <button onClick={() => handleClick(plus, '+')}>PLUS</button>
                <button onClick={() => handleClick(minus, '-')}>MINUS</button>
                <button onClick={() => handleClick(multiply, '*')}>MULTIPLY</button>
                <button onClick={() => handleClick(divide, '/')}>DIVIDE</button>
            </p>
            <p>
                <input type="number" value={one} onChange={handleChangeOne} />
                <span> {simbol} </span>
                <input type="number" value={two} onChange={handleChangeTwo} />
                <span> = {action ? action(one, two) : ''}</span>
            </p>
        </>
    );
}

Обратите внимание на строку кода, где мы задаем начальный state с использованием хука useState. Мы не может просто передать функцию plus, потому что useState может принимать в качестве аргумента функцию. И useState воспримет plus как функцию, которую надо выполнить один раз, чтобы получить начальный state. Поэтому мы передаем в качестве параметра функцию () => plus, которая при вызове как раз вернет начальный state в виде функции plus. С функцией setAction все происходит аналогично useState.

Наш калькулятор уже работает, но пока ничего особо интересного не было. Настало время добавить еще немного кода в функциональном стиле. Избавимся от функций handleChangeOne и handleChangeTwo, заменив их одной функцией handleChange.

import { useState } from 'react';

const plus = (a, b) => a + b;
const minus = (a, b) => a - b;
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;

const handleChange = (setter) => {
    return (event) => {
        const value = event.target.value;
        setter(value ? parseFloat(value) : 0);
    };
};

export default function Calculator() {
    const [one, setOne] = useState(0);
    const [two, setTwo] = useState(0);
    const [simbol, setSimbol] = useState('+');
    const [action, setAction] = useState(() => plus);

    const handleClick = (action, simbol) => {
        setAction(() => action);
        setSimbol(simbol);
    }

    return (
        <>
            <p>
                <button onClick={() => handleClick(plus, '+')}>PLUS</button>
                <button onClick={() => handleClick(minus, '-')}>MINUS</button>
                <button onClick={() => handleClick(multiply, '*')}>MULTIPLY</button>
                <button onClick={() => handleClick(divide, '/')}>DIVIDE</button>
            </p>
            <p>
                <input type="number" value={one} onChange={handleChange(setOne)} />
                <span> {simbol} </span>
                <input type="number" value={two} onChange={handleChange(setTwo)} />
                <span> = {action ? action(one, two) : ''}</span>
            </p>
        </>
    );
}

Функция handleChange принимает в качестве аргумента функцию-сеттер, которая обновляет в состоянии числа one и two. И возвращает анонимную функцию, которая при событии onChange будет вызвана — и, в свою очередь, вызовет setOne или setTwo.

Теперь изменим функцию handleClick — она будет вызываться не по событию onClick, а в момент рендера. И вернет анонимную функцию, которая как раз и будет вызвана по событию клика. И эта анонимная функция изменит переменные состояния action (функция операции) и simbol (символ операции) в момент клика.

import { useState } from 'react';

const plus = (a, b) => a + b;
const minus = (a, b) => a - b;
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;

const handleChange = (setter) => {
    return (event) => {
        const value = event.target.value;
        setter(value ? parseFloat(value) : 0);
    };
};

export default function Calculator() {
    const [one, setOne] = useState(0);
    const [two, setTwo] = useState(0);
    const [simbol, setSimbol] = useState('+');
    const [action, setAction] = useState(() => plus);

    const handleClick = (action, simbol) => {
        return () => {
            setAction(() => action);
            setSimbol(simbol);
        };
    };

    return (
        <>
            <p>
                <button onClick={handleClick(plus, '+')}>PLUS</button>
                <button onClick={handleClick(minus, '-')}>MINUS</button>
                <button onClick={handleClick(multiply, '*')}>MULTIPLY</button>
                <button onClick={handleClick(divide, '/')}>DIVIDE</button>
            </p>
            <p>
                <input type="number" value={one} onChange={handleChange(setOne)} />
                <span> {simbol} </span>
                <input type="number" value={two} onChange={handleChange(setTwo)} />
                <span> = {action ? action(one, two) : ''}</span>
            </p>
        </>
    );
}

Поиск: JavaScript • React.js • Web-разработка • Компонент • Функция

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.