В настоящее время Context — это очень мощный API, который часто не используется напрямую. Большинство проектов не будут использовать его напрямуюcreateContext
Затем передайте данные ниже, но используйте стороннюю библиотеку (react-redux).
Подумайте, часто ли он используется в проекте@connect(...)(Comp)
так же как<Provider value={store}><App /></Provider>
?
Что такое контекст
Контекст предоставляет способ передачи данных между деревьями компонентов без ручного добавления свойств на каждый уровень компонентов.
Данные верхнего уровня, которые необходимо передать некоторому глубокому компоненту черезprops
Проходить слой за слоем было бы очень утомительно, использование Context позволяет избежать явного прохождения через дерево компонентов слой за слоем.props
.
Пример использования контекста
import React, { Component, createContext, useConText } from 'react'
const ColorContext = createContext(null)
const { Provider, Consumer } = ColorContext
console.log('ColorContext', ColorContext)
console.log('Provider', Provider)
console.log('Consumer', Consumer)
class App extends Component {
constructor(props) {
super(props)
this.state = {
color: 'red',
background: 'cyan',
}
}
render() {
return <Provider value={this.state}>{this.props.children}</Provider>
}
}
function Article({ children }) {
return (
<App>
<h1>Context</h1>
<p>hello world</p>
{children}
</App>
)
}
function Paragraph({ color, background }) {
return (
<div style={{ backgroundColor: background }}>
<span style={{ color }}>text</span>
</div>
)
}
function TestContext() {
return (
<Article>
<Consumer>{state => <Paragraph {...state} />}</Consumer>
</Article>
)
}
export default TestContext
Эффект страницы
РаспечататьColorContext
,Provider
,Consumer
createContext
// createContext 可以让我们实现状态管理
// 还能够解决传递 Props drilling 的问题
// 假如一个子组件需要父组件的一个属性,但是中间间隔了好几层,这就会出现开发和维护的一个成本。这时候就可以通过这个 API 来解决
function createContext(defaultValue, calculateChangedBits) {
var context = {
?typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
// As a workaround to support multiple concurrent renderers, we categorize
// some renderers as primary and others as secondary. We only expect
// there to be two concurrent renderers at most: React Native (primary) and
// Fabric (secondary); React DOM (primary) and React ART (secondary).
// Secondary renderers store their context values on separate fields.
// 以下两个属性是为了适配多平台
_currentValue: defaultValue,
_currentValue2: defaultValue,
// Used to track how many concurrent renderers this context currently
// supports within in a single renderer. Such as parallel server rendering.
_threadCount: 0,
// These are circular
Provider: null,
Consumer: null
};
// 以下的代码很简单,就是在 context 上挂载 Provider 和 Consumer,让外部去使用
context.Provider = {
?typeof: REACT_PROVIDER_TYPE,
_context: context
};
var Consumer = {
?typeof: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits
};
context.Consumer = Consumer;
context._currentRenderer = null;
context._currentRenderer2 = null;
return context;
}
существуетreact
В пакете сгенерировано всего несколько объектов, которые относительно просты Давайте посмотрим, где это вступает в игру.
существуетConsumer
children
Отладчик используется в анонимной функции.
Просмотр стека вызовов
в основномnewChildren = render(newValue);
,newChildren
даConsumer
изchildren
Возвращаемое значение после вызова,render
то естьchildren
,newValue
ОтProvider
value
уступка имущества.
newProps
newValue
см. далееreadContext
реализация
let lastContextDependency: ContextDependency<mixed> | null = null;
let currentlyRenderingFiber: Fiber | null = null;
// 在 prepareToReadContext 函数
currentlyRenderingFiber = workInProgress;
export function readContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
let contextItem = {
context: ((context: any): ReactContext<mixed>),
observedBits: resolvedObservedBits,
next: null,
};
if (lastContextDependency === null) {
// This is the first dependency for this component. Create a new list.
lastContextDependency = contextItem;
currentlyRenderingFiber.contextDependencies = {
first: contextItem,
expirationTime: NoWork,
};
} else {
// Append a new context item.
lastContextDependency = lastContextDependency.next = contextItem;
}
}
// isPrimaryRenderer 为 true,定义的就是 true
// 实际就是一直会返回 context._currentValue
return isPrimaryRenderer ? context._currentValue : context._currentValue2;
}
пропустить середину, последнее предложениеreturn context._currentValue
,а также
Передайте его сверхуcontext
Значение получено
Я до сих пор не понимаю, почему контекст может передаваться с верхнего уровня на нижний, и я напишу статью, чтобы объяснить это после того, как ознакомлюсь с реализацией межкомпонентной передачи, 囧.
Контекст разработан, чтобы быть очень особенным
Provider
Consumer
два свойства контекста.
var context = {
?typeof: REACT_CONTEXT_TYPE,
_currentValue: defaultValue,
_currentValue2: defaultValue,
Provider: null,
Consumer: null
};
Provider
из?typeof
даREACT_PROVIDER_TYPE
, который поставляется с_context
атрибут, указывающий наcontext
Сам по себе, то есть его собственный сын имеет атрибут, указывающий на него самого! ! !
context.Provider = {
?typeof: REACT_PROVIDER_TYPE,
_context: context
};
Consumer
из?typeof
даREACT_CONTEXT_TYPE
, который также имеет_context
атрибут, а также его собственный сын имеет атрибут, указывающий на него самого! ! !
var Consumer = {
?typeof: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits
};
Таким образом, вы можете сделать предположение,Provider
Новое значение, назначенное атрибутом value, должно пройти_context
атрибут переданcontext
выше, изменено_currentValue
. такой же,Consumer
также на основе_context
понятноcontext
из_currentValue
,Потомrender(newValue)
воплощать в жизньchildren
функция.
useContext
useContext
Это функция, предоставляемая хуками реагирования, которая может упростить получение контекста.
См. код ниже
import React, { useContext, createContext } from 'react'
const NameCtx = createContext({ name: 'yuny' })
function Title() {
const { name } = useContext(NameCtx)
return <h1># {name}</h1>
}
function App() {
return (
<NameCtx.Provider value={{ name: 'lxfriday' }}>
<Title />
</NameCtx.Provider>
)
}
export default App
Мое начальное значение{name: 'yuny'}
, который фактически переназначает{name: 'lxfriday'}
, последняя страница показываетlxfriday
.
Исходный код, связанный с useContext
Первый взглядreactэкспортируется в упаковкеuseContext
/**
* useContext
* @param Context {ReactContext} createContext 返回的结果
* @param unstable_observedBits {number | boolean | void} 计算新老 context 变化相关的,useContext() second argument is reserved for future
* @returns {*} 返回的是 context 的值
*/
export function useContext<T>(
Context: ReactContext<T>,
unstable_observedBits: number | boolean | void,
) {
const dispatcher = resolveDispatcher();
return dispatcher.useContext(Context, unstable_observedBits);
}
// Invalid hook call. Hooks can only be called inside of the body of a function component.
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current;
return dispatcher;
}
/**
* Keeps track of the current dispatcher.
*/
const ReactCurrentDispatcher = {
/**
* @internal
* @type {ReactComponent}
*/
current: (null: null | Dispatcher),
};
посмотриDispatcher
, все, что связано с React Hooks.
опять такиreact-reconciler/src/ReactFiberHooks.jsв, естьHooksDispatcherOnMountInDEV
а такжеHooksDispatcherOnMount
,поясInDEV
должен быть вdevelopment
Окружение будет использовать его, а тот, что без него, будет использоваться в `производстве.
const HooksDispatcherOnMount: Dispatcher = {
readContext,
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
useDebugValue: mountDebugValue,
};
HooksDispatcherOnMountInDEV = {
// ...
useContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
return readContext(context, observedBits);
},
}
наверхуuseContext
проходить черезreadContext
возвращает значение контекста,readContext
Исходный код описан выше.
стек вызовов просмотра отладчика
ИзначальноuseContext
существуетHooksDispatcherOnMountInDEV
середина
readContext
середина
После подробного анализа приведенного выше исходного кода вы должны понимать создание контекста и значение контекста Дизайн контекста действительно прекрасен! !
Добро пожаловать в мой публичный аккаунт Yunyingsky
Регулярный углубленный анализ исходного кода React