Автор: Чен Цзюньшэн
React Hooks
Официально выпущен в версии React@16.8. Я недавно начал использовать его в одном или двух внутренних проектах компании.
не понимаюHooks
одноклассникиДокументация. Эта статья невернаHooks
Сделайте подробное введение и опишите только одно использованиеHooks
идеи.
Обычно мы пишемReact
Если это не особенно большое приложение, логика взаимодействия данных внешнего и внутреннего интерфейса несложна, поэтому мы можем напрямую писать компоненты в соответствии с обычным процессом для удовлетворения простых бизнес-сценариев. С углублением бизнес-сценариев наши компоненты постепенно становятся все больше и больше, а обмен данными между компонентами (то есть управление состоянием, но я предпочитаю называть это обменом данными) становится все более и более сложным. Итак, мы представилиRedux
поддерживать нашу все более сложную передачу данных.
идеи
Придерживаясь этой идеи, когда я разрабатывал приложение, я не вводил его в начале.Redux
, потому что сначала я думал, что это небольшой проект. С развитием проекта не все так просто.
Но это не так уж сложно. В это время я положил глаз наContext
тело.Context
Предназначенный для контекста, он обеспечиваетProvider
с однимConsumer
, то есть модель производитель/потребитель, которая обеспечиваетProvider
, следующие дочерние элементы проходятConsumer
потреблятьProvider
данные и методы.
Благодаря этой концепции мы разделяем один и тот же верхний уровень с компонентами на разных уровнях.Provider
, и компонент внутри используетConsumer
потреблять общие данные.
Когда мы сможем обмениваться данными, останется только один вопрос: как изменитьProvider
А данные в нем? ответ:useReducer
.
Что ж, есть идея, давайте ее реализовывать.
Пример
Предположим, у нас есть родительский элемент на определенном уровне, которому необходимо совместно использовать состояние, мы называем его Родительским, и есть два дочерних элемента между разными уровнями ниже родительского. Здесь для простого примера предполагается, что у обоих детей общая логика.
import React from "react"
function Parent() {
const colors = ['red', 'blue']
return (
<>
<Child1 color={colors[0]} />
<Child2 color={colors[1]} />
</>
)
}
function Child1(props) {
return (
<div style={{ background: props.color }}>I am {props.color}</div>
)
}
function Child2(props) {
return (
<div style={{ background: props.color }}>I am {props.color}</div>
)
}
Теперь мы построили такую подчиненную структуру.В настоящее время, передавая свойства подкомпонентам, можно добиться разделения состояния родительских компонентов. Но если уровень углубляется здесь, уровень, на котором мы проходим атрибуты, также будет углубляться. Это явно не то, чего мы хотим.
Теперь давайте представимContext
.
сначала черезcreateContext
метод для инициализации того, что нам нужноContext
.
import React, { createContext } from "react"
const Context = createContext({
colors: ['red', 'blue']
})
Затем мы вводим только сейчас в Parent and ChildContextи использоватьuseContext
Получить общие данные:
import React, { useContext, createContext } from "react"
const Context = createContext({
colors: []
})
function Parent() {
const initState = {
colors: ["red", "blue"]
}
return (
<Context.Provider value={{ colors: initState.colors }}>
<>
{/* 假装这些地方有着不同的层级 */}
<Child1 />
<Child2 />
</>
</Context.Provider>
)
}
function Child1(props) {
const { colors } = useContext(Context);
return (
<div style={{ background: colors[0] }}>
I am {colors[0]}
</div>
)
}
// 省略 Child2 代码,同 Child1 一致
Теперь просто получите данные и визуализируйте их, а затем сделайте еще один шаг и измените цвет, щелкнув элемент. Здесь нам нужно использоватьuseReducer
для имитации изменений триггера.
Сначала нам нуженreducerдля обработки инициированного изменения.
function reducer(state, action) {
const { colors } = action
if (action.type === "CHANGE_COLOR") {
return { colors: colors }
} else {
throw new Error()
}
}
Здесь я упростилactionконечно, вы также можете расширить его.
Теперь мы даемProvider
плюс предоставить способ изменитьdispatch.
import React, { useContext, createContext } from "react"
const Context = createContext({
colors: []
})
function Parent() {
const initState = {
colors: ["red", "blue"]
}
const [state, dispatch] = useReducer(reducer, initState)
return (
<Context.Provider value={{ colors: state.colors, dispatch: dispatch }}>
<>
{/* 假装这些地方有着不同的层级 */}
<Child1 />
<Child2 />
</>
</Context.Provider>
)
}
Затем дочерний компонент инициирует изменение:
function Child1(props) {
const { colors, dispatch } = useContext(Context)
return (
<div
style={{ background: colors[0] }}
onClick={() =>
dispatch({
type: "CHANGE_COLOR",
colors: ["yellow", "blue"]
})
}
>
I am {colors[0]}
</div>
)
}
// 省略 Child2 代码,同 Child1 一致
На этом небольшой обмен состояниями завершен. Это то, от чего мы избавляемсяRedux
Прототип идеи разделения состояния был реализован позже. Полный код и примеры см.tiny redux.
Передовой
В практических приложениях наши бизнес-сценарии будут более сложными, например, наши данные собираются динамически.
В этом случае можно поставитьProvider
Выньте его и инициализируйте, когда вернутся родительские данные.Context.
function Provider (props) {
const { colors } = props
const initState = {
colors,
}
const [state, dispatch] = useReducer(reducer, initState)
return (
<Context.Provider value={{ colors: state.colors, dispatch: dispatch }}>
{props.children}
</Context.Provider>
)
}
Затем мы делаем асинхронные операции в Parent и передаем динамические данные вProvider :
import React, { useState, useEffect } from "react"
function Parent (props) {
const [data, setData] = useState()
const [url, setUrl] = useState('https://example.com')
useEffect(() => {
fetch(url).then(res => setData(data))
}, [url])
if (!data) return <div>Loading ...</div>
return (
<Provider colors={data}>
<>
{/* 假装这些地方有着不同的层级 */}
<Child1 />
<Child2 />
</>
</Provider>
)
}
глубоко
Мы можем пойти еще дальше и сделать наш механизм управления состоянием более упорядоченным.
Во-первых, определим, что нам нужно на уровне компонентовContext. Допустим, мы здесь на верхнем уровне (то есть глобальном управлении состоянием).
import React from 'react'
// 创建我们需要的 Context
export const AppContext = React.createContext(null)
Тогда мы будемuseReducer
Возвращаемое значение передается непосредственно вAppContext.Provider.
import React, { useReducer } from 'react'
// 全局 Provider
export function AppProvider ({reducer, initValue, children}) {
return (
<AppContext.Provider value={useReducer(reducer, initValue)}>
{children}
</AppContext.Provider>
)
}
Наконец, добавьте пользовательскийhooks
получитьAppContextсостояние и методы. Напиши один раз, беги везде :)
import React, { useReducer, useContext } from 'react'
export const useAppState = () => useContext(AppContext)
Наконец нашstate.js
Полный код выглядит следующим образом:
import React, { useContext, useReducer } from 'react'
export const AppContext = React.createContext(null)
export function AppProvider ({reducer, initValue, children}) {
return (
<AppContext.Provider value={useReducer(reducer, initValue)}>
{children}
</AppContext.Provider>
)
}
export const useAppState = () => useContext(AppContext)
Компоненты используют:
import { AppProvider, useAppState } from "./state"
function App() {
const initState = {
colors: ["red", "blue"]
}
function reducer(state, action) {
const { colors } = action;
if (action.type === "CHANGE_COLOR") {
return { colors: colors };
} else {
throw new Error();
}
}
return (
<AppProvider initValue={initState} reducer={reducer}>
<div>
{/* 假装这些地方有着不同的层级 */}
<Child1 />
<Child2 />
</div>
</AppProvider>
)
}
function Child1(props) {
const [state, dispatch] = useAppState()
return (
<div
style={{ background: state.colors[0] }}
onClick={() =>
dispatch({
type: "CHANGE_COLOR",
colors: ["yellow", "blue"]
})
}
>
I am {state.colors[0]}
</div>
)
}
Полный код и примеры см.tiny redux.
Эпилог
Вы даже можете поместить такой небольшой механизм управления состоянием в компонент, вместо того, чтобы помещать его в что-то вродеRedux
к глобальной среде. Это делает приложения, которые мы пишем, более гибкими, а не вслепуюstore
потерянное состояние. Конечно, вы также можете написатьAppProviderуправлять глобальным состоянием,React Hooks
+ Context
дал нам такое удобство.
Крючки вкусные!
Небольшие программы также можно разрабатывать с помощью хуков, подробнее«Рефакторинг ваших апплетов с помощью React Hooks»