docs(post): update post sandpack

This commit is contained in:
xfy
2024-04-16 16:12:18 +08:00
parent 16f5a33b36
commit ab9169aec9
4 changed files with 366 additions and 21 deletions

View File

@ -14,15 +14,22 @@ const RUASandpack = ({ ...rest }: Props) => {
if (!mounted) {
return (
<div className="my-2 min-h-[402px]">
<Sandpack {...rest} />
<div className="my-2 rounded-[0.5em] overflow-hidden">
<Sandpack
{...rest}
options={{
showConsole: true,
showConsoleButton: true,
editorHeight: '512px',
}}
/>
</div>
);
}
return (
<>
<div className="my-2 min-h-[402px]">
<div className="my-2 rounded-[0.5em] overflow-hidden">
<Sandpack
{...rest}
theme={
@ -30,6 +37,12 @@ const RUASandpack = ({ ...rest }: Props) => {
(theme ?? 'latte') as keyof typeof THEME_CATPUCCIN_MAP
]
}
options={{
showConsole: true,
showConsoleButton: true,
editorHeight: '512px',
showRefreshButton: true,
}}
/>
</div>
</>

View File

@ -24,6 +24,14 @@ import {
useSyncExternalStore,
useTransition,
} from './sandpack/react18-new-hooks';
import {
app1,
app2,
app3,
signal1,
signal2,
signal3,
} from './sandpack/automatic-dependency-collect';
const data = {
sandpack: {
@ -58,6 +66,14 @@ const data = {
MultiStore,
Reducer,
},
'automatic-dependency-collect': {
signal1,
app1,
signal2,
app2,
signal3,
app3,
},
},
};

View File

@ -96,24 +96,17 @@ const useState: UseState = (value) => {
使用它的方式和官方 hooks 没有区别:
```tsx
function App() {
console.log('App updated');
const [count, setCount] = useState(0);
return (
<>
<button
onClick={() => {
setCount((d) => d + 1);
<RUASandpack
template="react-ts"
files={{
'/Button.tsx': {
code: sandpack.common.Button,
hidden: true,
},
'/signal.ts': sandpack['automatic-dependency-collect'].signal1,
'/App.tsx': sandpack['automatic-dependency-collect'].app1,
}}
>
Update {count()}
</button>
</>
);
}
```
/>
### useEffect
@ -248,6 +241,18 @@ const useEffect = (callback: () => void) => {
};
```
<RUASandpack
template="react-ts"
files={{
'/Button.tsx': {
code: sandpack.common.Button,
hidden: true,
},
'/signal.ts': sandpack['automatic-dependency-collect'].signal2,
'/App.tsx': sandpack['automatic-dependency-collect'].app2,
}}
/>
### useMemo
通过 `useEffect` 实现的自动依赖追踪,我们就可以轻松的实现一个自动追踪依赖的 `useMemo`
@ -259,3 +264,15 @@ const useMemo = <T>(callback: () => T) => {
return s;
};
```
<RUASandpack
template="react-ts"
files={{
'/Button.tsx': {
code: sandpack.common.Button,
hidden: true,
},
'/signal.ts': sandpack['automatic-dependency-collect'].signal3,
'/App.tsx': sandpack['automatic-dependency-collect'].app3,
}}
/>

View File

@ -0,0 +1,299 @@
export const signal1 = `import { useRef, useState as useUpdate } from "react";
export type Getter<T> = () => T;
export type Setter<T> = (newValue: T | ((oldValue: T) => T)) => void;
export type UseState = <T>(value: T) => [Getter<T>, Setter<T>];
export const useState: UseState = (value) => {
const state = useRef(value);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, update] = useUpdate({});
const getter: Getter<typeof value> = () => {
return state.current;
};
const setter: Setter<typeof value> = (updater) => {
const newState =
updater instanceof Function ? updater(state.current) : updater;
if (state.current === newState) return;
state.current = newState;
update({});
};
return [getter, setter];
};
`;
export const app1 = `import { useState } from "./signal";
import Button from './Button';
function App() {
console.log('App updated');
const [count, setCount] = useState(0);
return (
<>
<Button
onClick={() => {
setCount((d) => d + 1);
}}
>
Update {count()}
</Button>
</>
);
}
export default App;
`;
export const signal2 = `/* eslint-disable react-hooks/exhaustive-deps */
import { useRef, useState as useUpdate, useEffect as useMounted } from 'react';
export type Getter<T> = () => T;
export type Setter<T> = (newValue: T | ((oldValue: T) => T)) => void;
export type UseState = <T>(value: T) => [Getter<T>, Setter<T>];
export type Subs = Set<Effect>;
export type Effect = {
// 用于执行 useEffect 回调函数
execute: () => void;
// 保存该 useEffect 依赖的 state 对应的 subs 集合
deps: Set<Subs>;
};
const effectStack: Effect[] = [];
const subscribe = (effect: Effect, subs: Subs) => {
// 建立订阅关系
subs.add(effect);
// 建立依赖关系
effect.deps.add(subs);
};
const useState: UseState = (value) => {
const state = useRef(value);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, update] = useUpdate({});
const subs = useRef(new Set<Effect>());
const getter: Getter<typeof value> = () => {
// 获取刚刚送入栈中的 effect
const effect = effectStack[effectStack.length - 1];
if (effect) {
// 建立订阅发布关系
subscribe(effect, subs.current);
}
return state.current;
};
const setter: Setter<typeof value> = (updater) => {
const newState =
updater instanceof Function ? updater(state.current) : updater;
if (state.current === newState) return;
state.current = newState;
update({});
// 通知所有订阅 effect 状态变化
for (const sub of [...subs.current]) {
sub.execute();
}
};
return [getter, setter];
};
const cleanup = (effect: Effect) => {
for (const subs of effect.deps) {
subs.delete(effect);
}
effect.deps.clear();
};
const useEffect = (callback: () => void) => {
const execute = () => {
// 重制依赖
cleanup(effect);
// 添加到副作用列表
effectStack.push(effect);
try {
callback();
} finally {
effectStack.pop();
}
};
const effect: Effect = {
execute,
deps: new Set(),
};
useMounted(() => {
// 调用时执行
execute();
}, []);
};
export { useState, useEffect };
`;
export const app2 = `import { useState, useEffect } from "./signal";
import Button from './Button';
function App() {
console.log('App updated');
const [count, setCount] = useState(0);
console.log('App updated');
useEffect(() => {
console.log('count is ', count());
});
useEffect(() => {
console.log('effect update');
});
return (
<>
<Button
onClick={() => {
setCount((d) => d + 1);
}}
>
Update {count()}
</Button>
</>
);
}
export default App;
`;
export const signal3 = `/* eslint-disable react-hooks/exhaustive-deps */
import { useRef, useState as useUpdate, useEffect as useMounted } from 'react';
export type Getter<T> = () => T;
export type Setter<T> = (newValue: T | ((oldValue: T) => T)) => void;
export type UseState = <T>(value: T) => [Getter<T>, Setter<T>];
export type Subs = Set<Effect>;
export type Effect = {
// 用于执行 useEffect 回调函数
execute: () => void;
// 保存该 useEffect 依赖的 state 对应的 subs 集合
deps: Set<Subs>;
};
const effectStack: Effect[] = [];
const subscribe = (effect: Effect, subs: Subs) => {
// 建立订阅关系
subs.add(effect);
// 建立依赖关系
effect.deps.add(subs);
};
const useState: UseState = (value) => {
const state = useRef(value);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, update] = useUpdate({});
const subs = useRef(new Set<Effect>());
const getter: Getter<typeof value> = () => {
// 获取刚刚送入栈中的 effect
const effect = effectStack[effectStack.length - 1];
if (effect) {
// 建立订阅发布关系
subscribe(effect, subs.current);
}
return state.current;
};
const setter: Setter<typeof value> = (updater) => {
const newState =
updater instanceof Function ? updater(state.current) : updater;
if (state.current === newState) return;
state.current = newState;
update({});
// 通知所有订阅 effect 状态变化
for (const sub of [...subs.current]) {
sub.execute();
}
};
return [getter, setter];
};
const cleanup = (effect: Effect) => {
for (const subs of effect.deps) {
subs.delete(effect);
}
effect.deps.clear();
};
const useEffect = (callback: () => void) => {
const execute = () => {
// 重制依赖
cleanup(effect);
// 添加到副作用列表
effectStack.push(effect);
try {
callback();
} finally {
effectStack.pop();
}
};
const effect: Effect = {
execute,
deps: new Set(),
};
useMounted(() => {
// 调用时执行
execute();
}, []);
};
const useMemo = <T>(callback: () => T) => {
const [s, set] = useState(callback());
useEffect(() => set(callback()));
return s;
};
export { useState, useEffect, useMemo };
`;
export const app3 = `import { useState, useEffect, useMemo } from "./signal";
import Button from './Button';
function App() {
console.log('App updated');
const [count, setCount] = useState(0);
console.log('App updated');
useEffect(() => {
console.log('count is ', count());
});
useEffect(() => {
console.log('effect update');
});
const double = useMemo(() => count() * 2);
return (
<>
<Button
onClick={() => {
setCount((d) => d + 1);
}}
>
Update {count()}
</Button>
<Button disabled>
Double {double()}
</Button>
</>
);
}
export default App;
`;