Существуют различные способы управления данными в приложении. Одним из способов управления данными в приложении ReactJs является использование концепции prop drilling. Prop drilling требует, чтобы данные передавались через каждый родительский компонент в дочерний. Этот метод может быть громоздким и не совсем масштабируемым.
В этой статье мы рассмотрим концепцию Context API, которая упрощает управление состоянием приложения. Сначала нам нужно понять, что такое контекст, как он может быть реализован в приложении React, а затем мы создадим прототип банковского приложения для правильного понимания. Для правильного понимания этой статьи потребуются некоторые знания React и JavaScript. Давайте сначала дадим определение контекста. Прежде чем приступить к этому, вы должны иметь базовые знания о ReactJs.
Контекст
Context предоставляет возможность передавать данные по дереву компонентов без необходимости передавать реквизиты на каждом уровне вниз. Это означает, что управление данными осуществляется глобально. С помощью Context данные могут быть установлены глобально; при этом они доступны непосредственно любому из дочерних компонентов без необходимости передавать их через каждый родительский элемент. Проще говоря, Context помогает нам избежать вложенности на уровне различных компонентов для доступа к данным.
У Context есть несколько API, а именно: React.createContext, Context.Cosumer, Context.Provider и т.д. В этой статье мы сосредоточимся на React.createContext и Context.Provider.
Давайте создадим наше приложение в следующих шагах. Создадим react-приложение с помощью create-react-app.
Мы будем использовать компонент Material UI для стилизации различных интерфейсов.
Наше приложение должно показать, как управляется состояние на экранах депозита, баланса и снятия средств в нашем финансовом приложении.
После создания приложения fakebank с помощью create-react-app, в папке src мы создадим папку components, в которой будут храниться все наши папки, включая компонент context. В папке компонента создадим файл context.js. Папка context — это место, где происходит управление всем состоянием приложения.
В компонент контекста будут импортированы некоторые компоненты из react, а именно useState и useContext.
import React from {useState, useContext}
Далее мы вызываем метод createContext, который обеспечивает передачу данных по дереву компонентов без необходимости вручную передавать реквизиты на каждом уровне.
import React from {useState, useContext}
import React, { useState, useContext } from "react";
const AppContext = React.createContext();
Далее мы создадим компонент, через который будут передаваться дочерние реквизиты; мы также установим начальные состояния экрана баланса, вывода средств и депозита соответственно.
import React, { useState, useContext } from "react";
const AppContext = React.createContext();
const AppProvider = ({ children }) => {
const initialValues = { balance: 0, deposit: 0, withdrawal: 0 };
const [inputValue, setInputValue] = useState(initialValues);
let walletBalance = inputValue.balance;
let amountDeposit = inputValue.deposit;
let amountWithdrawn = inputValue.withdrawal;
Функция выше будет возвращать дочерние реквизиты, обернутые AppContext.Provider. Провайдер позволяет потребляющим компонентам подписываться на изменения в контексте. Далее мы экспортируем наши компоненты AppContext и AppProvider. Но сначала мы настроим пользовательский хук, чтобы сделать наш код чище. (ПРИМЕЧАНИЕ: Вы можете не использовать пользовательский хук)
import React, { useState, useContext } from "react";
const AppContext = React.createContext();
const AppProvider = ({ children }) => {
const initialValues = { balance: 0, deposit: 0, withdrawal: 0 };
const [inputValue, setInputValue] = useState(initialValues);
let walletBalance = inputValue.balance;
let amountDeposit = inputValue.deposit;
let amountWithdrawn = inputValue.withdrawal;
return (
<AppContext.Provider>
{children}
</AppContext.Provider>
);
}
//custom hook
const useGlobalContext = () => {
return useContext(AppContext);
};
export { useGlobalContext, AppProvider };
Мы обернем компонент App в файле index.js экспортированным AppProvider.
<React.StrictMode>
<AppProvider>
<App />
</AppProvider>
</React.StrictMode>
Далее передадим значения начального состояния нашего приложения компоненту AppProvider.
import React, { useState, useContext } from "react";
const AppContext = React.createContext();
const AppProvider = ({ children }) => {
const initialValues = { balance: 0, deposit: 0, withdrawal: 0 };
const [inputValue, setInputValue] = useState(initialValues);
let walletBalance = inputValue.balance;
let amountDeposit = inputValue.deposit;
let amountWithdrawn = inputValue.withdrawal;
return (
<AppContext.Provider value={{
walletBalance,
amountDeposit,
amountWithdrawn,
}}>
{children}
</AppContext.Provider>
);
}
//custom hook
const useGlobalContext = () => {
return useContext(AppContext);
};
export { useGlobalContext, AppProvider };
Далее нам нужно создать интерфейсы экранов пополнения счета, снятия средств и баланса. Как было сказано ранее, для создания интерфейсов нашего приложения мы будем использовать фреймворк Material UI.
import React from "react";
import {
Box,
Card,
CardContent,
Typography,
CardActions,
Button,
Divider,
} from "@mui/material";
const Deposit = () => {
return (
<Box
sx={{
marginTop: "10rem",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Card sx={{ minWidth: 700, background: "#A52A2A", color: "white" }}>
<CardContent>
<Typography
sx={{ fontSize: 20, textAlign: "center", fontWeight: "bold" }}
gutterBottom
>
Deposit
</Typography>
<Divider color="white" />
<Box
sx={{ display: "flex", justifyContent: "space-around", mt: "1rem" }}
>
<Typography sx={{ mb: 1.5, color: "white" }} color="text.secondary">
Balance
</Typography>
<Typography sx={{ mb: 1.5, color: "white" }} color="text.secondary">
$100
</Typography>
</Box>
<Typography sx={{ fontWeight: "bold" }}>Deposit Amount</Typography>
<form>
<input
type="text"
id="deposit"
name="deposit"
value="deposit"
/>
</form>
</CardContent>
<CardActions>
<Button
variant="contained"
sx={{ background: "white", color: "black" }}
>
Deposit
</Button>
</CardActions>
</Card>
</Box>
);
};
export default Deposit;
Приведенный выше интерфейс предназначен для экрана пополнения счета; тот же процесс будет повторен для других экранов. Баланс выше жестко закодирован с суммой $100, позже он будет динамически изменяться.
После создания интерфейсов, в контекстном компоненте необходимо создать обработчики событий для обработки изменений, которые будут происходить с формами ввода, где будут вводиться суммы пополнения и снятия; это будет обрабатываться в контекстном компоненте.
import React, { useState, useContext } from "react";
const AppContext = React.createContext();
const AppProvider = ({ children }) => {
const initialValues = { balance: 0, deposit: 0, withdrawal: 0 };
const [inputValue, setInputValue] = useState(initialValues);
let walletBalance = inputValue.balance;
let amountDeposit = inputValue.deposit;
let amountWithdrawn = inputValue.withdrawal;
//handle input change
const handleChange = (e) => {
const { name, value } = e.target;
setInputValue({ ...inputValue, [name]: value });
};
return (
<AppContext.Provider
value={{
walletBalance,
amountDeposit,
amountWithdrawn,
handleChange,
}}
>
{children}
</AppContext.Provider>
);
};
//custom hook
const useGlobalContext = () => {
return useContext(AppContext);
};
export { useGlobalContext, AppProvider };
Пользовательский хук будет деструктурирован в каждом из экранов для передачи значений в контекстный компонент.
const { walletBalance, amountDeposit, handleChange } =
useGlobalContext();
Ниже показаны значения, передаваемые в компонент deposit.
В этой статье мы рассмотрели, как контекстный API хук может быть использован для поддержания состояния в приложении react, процесс размещения состояния приложения в одном компоненте, что позволяет легко передавать данные через различные представления.
import React from "react";
import {
Box,
Card,
CardContent,
Typography,
CardActions,
Button,
Divider,
} from "@mui/material";
import { useGlobalContext } from "./context";
const Deposit = () => {
const { walletBalance, amountDeposit, handleChange } =
useGlobalContext();
return (
<Box
sx={{
marginTop: "10rem",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Card sx={{ minWidth: 700, background: "#A52A2A", color: "white" }}>
<CardContent>
<Typography
sx={{ fontSize: 20, textAlign: "center", fontWeight: "bold" }}
gutterBottom
>
Deposit
</Typography>
<Divider color="white" />
<Box
sx={{ display: "flex", justifyContent: "space-around", mt: "1rem" }}
>
<Typography sx={{ mb: 1.5, color: "white" }} color="text.secondary">
Balance
</Typography>
<Typography sx={{ mb: 1.5, color: "white" }} color="text.secondary">
{walletBalance}
</Typography>
</Box>
<Typography sx={{ fontWeight: "bold" }}>Deposit Amount</Typography>
<form>
<input
type="text"
id="deposit"
name="deposit"
value={amountDeposit}
onChange={handleChange}
/>
</form>
</CardContent>
<CardActions>
<Button
variant="contained"
sx={{ background: "white", color: "black" }}
>
Deposit
</Button>
</CardActions>
</Card>
</Box>
);
};
export default Deposit;
Это будет повторено на двух других экранах, а именно: вывод средств и баланс.
Далее необходимо обработать логику, которая отслеживает депозит, сумму и баланс. Помните, что любая логика обрабатывается в компоненте контекста.
//handle incrementing the deposit made to update balance
const handleAmount = (e) => {
e.preventDefault();
if (regex.test(amountDeposit)) {
walletBalance += parseInt(amountDeposit);
inputValue.balance = walletBalance;
setInputValue({ ...inputValue, walletBalance });
amountDeposit = "";
inputValue.deposit = amountDeposit;
setInputValue({ ...inputValue, amountDeposit });
} else {
alert("You have entered an invalid character!!!");
}
};
Функции обновляют баланс после внесения депозита.
//handle withdrawals
const withdrawalHandler = (e) => {
e.preventDefault();
if (regex.test(amountWithdrawn)) {
walletBalance -= parseInt(amountWithdrawn);
inputValue.balance = walletBalance;
setInputValue({ ...inputValue, walletBalance });
amountWithdrawn = "";
inputValue.withdrawal = amountWithdrawn;
setInputValue({ ...inputValue, amountWithdrawn });
} else {
alert("You have entered an invalid character!!!");
}
};
Передайте сумму handleAmount и сумму withdrawalHandler в провайдер и получите ее деструктуризацию в соответствующих компонентах.
<AppContext.Provider
value={{
walletBalance,
amountDeposit,
amountWithdrawn,
handleChange,
handleAmount,
withdrawalHandler,
}}
>
{children}
</AppContext.Provider>
UseGlobalContext для вывода и ввода средств должен выглядеть следующим образом:
const { walletBalance, amountDeposit, handleChange, handleAmount } =
useGlobalContext();
и
const { walletBalance, amountWithdrawn, handleChange, withdrawalHandler } =
useGlobalContext();
Таким образом, компоненты баланса:
import React from "react";
import {
Box,
Card,
CardContent,
Typography,
Divider,
} from "@mui/material";
import { useGlobalContext } from "./context";
const Balance = () => {
const { walletBalance } = useGlobalContext();
return (
<Box
sx={{
marginTop: "10rem",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Card sx={{ minWidth: 700, background: "#A52A2A", color: "white" }}>
<CardContent>
<Typography
sx={{ fontSize: 20, textAlign: "center", fontWeight: "bold" }}
gutterBottom
>
Balance
</Typography>
<Divider color="white" />
<Box
sx={{ display: "flex", justifyContent: "space-around", mt: "1rem" }}
>
<Typography sx={{ mb: 1.5, color: "white" }} color="text.secondary">
Balance
</Typography>
<Typography sx={{ mb: 1.5, color: "white" }} color="text.secondary">
{walletBalance}
</Typography>
</Box>
</CardContent>
</Card>
</Box>
);
};
export default Balance;
В этой статье мы смогли изучить React Context API на практике, создав финансовое приложение, которое помогает вести учет депозитов, сумм и снятия средств. Вы можете углубиться в детали, прочитав официальную документацию по react