React Context передает данные между различными уровнями вашего приложения без детализации реквизитов. Он предоставляет способ передачи данных через дерево компонентов.
Опора Сверление
Чтобы лучше понять контекст реакции, мы должны сначала понять проблему, которую он решает. Детализация свойств происходит, когда мы продолжаем бесконечно передавать данные дочерним компонентам, даже если некоторые из этих компонентов не используют их; они получают его, чтобы передать его своим детям.
Возьмем типичный пример, приложение-счетчик.
export default function App() { const [counter, setCounter] = useState(0); return ( <div> <AddOneButton setCounter={setCounter} /> <Counter counter={counter} /> </div> ); }
У него есть два дочерних компонента: один для увеличения счетчика, а другой для отображения текущего счетчика.
const AddOneButton = ({setCounter}) => { return ( <button onClick={() => setCounter((value) => value + 1)}>Add One</button> ) } const Counter = ({counter}) => { return ( <p>Count: {counter}</p> ) }
Теперь предположим, что у нас есть новый компонент с именем Container
, который обертывает AddOneButton
.
const Container = ({setCounter}) => { return <AddOneButton setCounter={setCounter} /> }
И теперь внутри App
мы визуализируем новый компонент Container
.
<Container setCounter={setCounter} />
Теперь ясно, что Container
остро не нужна функцияsetCounter
, мы передали ее только потому, что она нужна AddOneButton
, и это то, что известно как 'Prop Drilling', вот почему нам нужен Context
Api.
ПРИМЕЧАНИЕ
В этом случае мы можем смягчить использование сверления реквизита, сделав компонент Container
общим компонентом и передав AddOneButton
в качестве дочернего.
const Container = ({children}) => { return ( {children} ) } export default function App() { const [counter, setCounter] = useState(0); return ( <div> <Container> <AddOneButton setCounter={setCounter} /> </Container> <Counter counter={counter} /> </div> ); }
Контекст с useState
теперь, когда мы разобрались с просверливанием пропеллеров, мы можем начать использовать Context
Api, но перед этим нам нужно решить, каким образом мы будем реализовывать Context; в этом разделе в основном есть два способа либо с useState
, либо с useReducer
, мы сделаем это с помощью useState
, начнем с импорта необходимых модулей.
import {createContext, useContext} from "react";
Затем мы создаем Context и его провайдера.
export const CounterContext = createContext(null); export const CounterContextProvider = ({ children }) => ( <CounterContext.Provider value={useState(0)}> {children} </CounterContext.Provider> );
функция createContext
инициализирует контекст, поставщик будет обертывать наше приложение (только компоненты внутри поставщика будут иметь доступ к данным), и, поскольку мы используем контекстный API, нам не нужно ничего передавать компонентам Container
или Counter
.
export default function App() { return ( <CounterContextProvider> <Container /> <Counter /> </CounterContextProvider> ); }
Теперь для доступа к данным мы будем использовать хук useContext
.
const Container = () => { return <AddOneButton />; }; const AddOneButton = () => { const [, setCounter] = useContext(CounterContext); return ( <button onClick={() => setCounter((value) => value + 1)}>Add One</button> ); }; const Counter = () => { const [counter] = useContext(CounterContext); return <p>Count: {counter}</p>; };
Контекст с useReducer
Примечание. Рекомендуется использовать useReducer
, если наше состояние более сложное.
Начнем с импорта необходимых модулей.
import { createContext, useContext, useReducer } from "react";
Теперь давайте создадим редьюсер и подключим его к нашим контекстным данным.
const reducer = (state, action) => { switch(action.type) { case "increment": return state + 1; default: return state } } export const CounterContext = createContext(null); export const CounterContextProvider = ({ children }) => ( <CounterContext.Provider value={useReducer(reducer, 0)}> {children} </CounterContext.Provider> );
Хук возвращает кортеж, первый элемент — это значение, а второй — функция отправки.
const Container = () => { return <AddOneButton />; }; const AddOneButton = () => { const [, dispatch] = useContext(CounterContext); return ( <button onClick={() => dispatch({type: "increment"})}>Add One</button> ); }; const Counter = () => { const [counter] = useContext(CounterContext); return <p>Count: {counter}</p>; };
И в основном так работает контекстный API.