Skip to content

Commit 18d2504

Browse files
committed
feat: context
1 parent 4e29af0 commit 18d2504

File tree

16 files changed

+161
-9
lines changed

16 files changed

+161
-9
lines changed

demos/context/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<title>context测试</title>
9+
</head>
10+
11+
<body>
12+
<div id="root"></div>
13+
<script type="module" src="main.tsx"></script>
14+
</body>
15+
16+
</html>

demos/context/main.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useState, createContext, useContext } from 'react';
2+
import ReactDOM from 'react-dom/client';
3+
4+
const ctxA = createContext('deafult A');
5+
const ctxB = createContext('default B');
6+
7+
function App() {
8+
return (
9+
<ctxA.Provider value={'A0'}>
10+
<ctxB.Provider value={'B0'}>
11+
<ctxA.Provider value={'A1'}>
12+
<Cpn />
13+
</ctxA.Provider>
14+
</ctxB.Provider>
15+
<Cpn />
16+
</ctxA.Provider>
17+
);
18+
}
19+
20+
function Cpn() {
21+
const a = useContext(ctxA);
22+
const b = useContext(ctxB);
23+
return (
24+
<div>
25+
A: {a} B: {b}
26+
</div>
27+
);
28+
}
29+
30+
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
31+
<App />
32+
);

demos/context/vite-env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"main": "index.js",
66
"scripts": {
77
"build:dev": "rm -rf dist && rollup --config scripts/rollup/dev.config.js",
8-
"demo": "vite serve demos/ref --config scripts/vite/vite.config.js --force",
8+
"demo": "vite serve demos/context --config scripts/vite/vite.config.js --force",
99
"lint": "eslint --ext .ts,.jsx,.tsx --fix --quiet ./packages",
1010
"test": "jest --config scripts/jest/jest.config.js"
1111
},

packages/react-reconciler/src/beginWork.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import { renderWithHooks } from './fiberHooks';
55
import { Lane } from './fiberLanes';
66
import { processUpdateQueue, UpdateQueue } from './updateQueue';
77
import {
8+
ContextProvider,
89
Fragment,
910
FunctionComponent,
1011
HostComponent,
1112
HostRoot,
1213
HostText
1314
} from './workTags';
1415
import { Ref } from './fiberFlags';
16+
import { pushProvider } from './fiberContext';
1517

1618
// 递归中的递阶段
1719
export const beginWork = (wip: FiberNode, renderLane: Lane) => {
@@ -27,6 +29,8 @@ export const beginWork = (wip: FiberNode, renderLane: Lane) => {
2729
return updateFunctionComponent(wip, renderLane);
2830
case Fragment:
2931
return updateFragment(wip);
32+
case ContextProvider:
33+
return updateContextProvider(wip);
3034
default:
3135
if (__DEV__) {
3236
console.warn('beginWork未实现的类型');
@@ -36,6 +40,18 @@ export const beginWork = (wip: FiberNode, renderLane: Lane) => {
3640
return null;
3741
};
3842

43+
function updateContextProvider(wip: FiberNode) {
44+
const providerType = wip.type;
45+
const context = providerType._context;
46+
const newProps = wip.pendingProps;
47+
48+
pushProvider(context, newProps.value);
49+
50+
const nextChildren = newProps.children;
51+
reconcileChildren(wip, nextChildren);
52+
return wip.child;
53+
}
54+
3955
function updateFragment(wip: FiberNode) {
4056
const nextChildren = wip.pendingProps;
4157
reconcileChildren(wip, nextChildren);

packages/react-reconciler/src/childFibers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ function ChildReconciler(shouldTrackEffects: boolean) {
188188
if (
189189
Array.isArray(element) ||
190190
typeof element === 'string' ||
191-
typeof element === 'number'
191+
typeof element === 'number' ||
192+
element === undefined ||
193+
element === null
192194
) {
193195
return index;
194196
}

packages/react-reconciler/src/completeWork.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import {
1212
HostText,
1313
HostComponent,
1414
FunctionComponent,
15-
Fragment
15+
Fragment,
16+
ContextProvider
1617
} from './workTags';
18+
import { popProvider } from './fiberContext';
1719

1820
function markUpdate(fiber: FiberNode) {
1921
fiber.flags |= Update;
@@ -76,6 +78,11 @@ export const completeWork = (wip: FiberNode) => {
7678
case Fragment:
7779
bubbleProperties(wip);
7880
return null;
81+
case ContextProvider:
82+
const context = wip.type._context;
83+
popProvider(context);
84+
bubbleProperties(wip);
85+
return null;
7986
default:
8087
if (__DEV__) {
8188
console.warn('未处理的completeWork情况', wip);

packages/react-reconciler/src/fiber.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Props, Key, Ref, ReactElementType } from 'shared/ReactTypes';
22
import {
3+
ContextProvider,
34
Fragment,
45
FunctionComponent,
56
HostComponent,
@@ -10,6 +11,7 @@ import { Container } from 'hostConfig';
1011
import { Lane, Lanes, NoLane, NoLanes } from './fiberLanes';
1112
import { Effect } from './fiberHooks';
1213
import { CallbackNode } from 'scheduler';
14+
import { REACT_PROVIDER_TYPE } from 'shared/ReactSymbols';
1315

1416
export class FiberNode {
1517
type: any;
@@ -134,6 +136,11 @@ export function createFiberFromElement(element: ReactElementType): FiberNode {
134136
if (typeof type === 'string') {
135137
// <div/> type: 'div'
136138
fiberTag = HostComponent;
139+
} else if (
140+
typeof type === 'object' &&
141+
type.$$typeof === REACT_PROVIDER_TYPE
142+
) {
143+
fiberTag = ContextProvider;
137144
} else if (typeof type !== 'function' && __DEV__) {
138145
console.warn('为定义的type类型', element);
139146
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ReactContext } from 'shared/ReactTypes';
2+
3+
let prevContextValue: any = null;
4+
const prevContextValueStack: any[] = [];
5+
6+
export function pushProvider<T>(context: ReactContext<T>, newValue: T) {
7+
prevContextValueStack.push(prevContextValue);
8+
9+
prevContextValue = context._currentValue;
10+
context._currentValue = newValue;
11+
}
12+
13+
export function popProvider<T>(context: ReactContext<T>) {
14+
context._currentValue = prevContextValue;
15+
16+
prevContextValue = prevContextValueStack.pop();
17+
}

packages/react-reconciler/src/fiberHooks.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Dispatch } from 'react/src/currentDispatcher';
22
import { Dispatcher } from 'react/src/currentDispatcher';
33
import currentBatchConfig from 'react/src/currentBatchConfig';
44
import internals from 'shared/internals';
5-
import { Action } from 'shared/ReactTypes';
5+
import { Action, ReactContext } from 'shared/ReactTypes';
66
import { FiberNode } from './fiber';
77
import { Flags, PassiveEffect } from './fiberFlags';
88
import { Lane, NoLane, requestUpdateLane } from './fiberLanes';
@@ -82,14 +82,16 @@ const HooksDispatcherOnMount: Dispatcher = {
8282
useState: mountState,
8383
useEffect: mountEffect,
8484
useTransition: mountTransition,
85-
useRef: mountRef
85+
useRef: mountRef,
86+
useContext: readContext
8687
};
8788

8889
const HooksDispatcherOnUpdate: Dispatcher = {
8990
useState: updateState,
9091
useEffect: updateEffect,
9192
useTransition: updateTransition,
92-
useRef: updateRef
93+
useRef: updateRef,
94+
useContext: readContext
9395
};
9496

9597
function mountEffect(create: EffectCallback | void, deps: EffectDeps | void) {
@@ -379,3 +381,12 @@ function mountWorkInProgressHook(): Hook {
379381
}
380382
return workInProgressHook;
381383
}
384+
385+
function readContext<T>(context: ReactContext<T>): T {
386+
const consumer = currentlyRenderingFiber;
387+
if (consumer === null) {
388+
throw new Error('只能在函数组件中调用useContext');
389+
}
390+
const value = context._currentValue;
391+
return value;
392+
}

0 commit comments

Comments
 (0)