React.js. Функциональное программирование
03.09.2021
Теги: JavaScript • React.js • Web-разработка • Компонент • Функция
Функциональное программирование — это способ программирования, при котором можно передавать функции в качестве параметров другим функциям, а также возвращать их как значения. Так как функции в 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-разработка • Компонент • Функция