mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-15 16:51:37 +00:00
Rename post folder name
This commit is contained in:
302
content/sandpack/build-own-store-with-usesyncexternalstore.ts
Normal file
302
content/sandpack/build-own-store-with-usesyncexternalstore.ts
Normal file
@ -0,0 +1,302 @@
|
||||
export const multi = `import { useSyncExternalStore } from 'react';
|
||||
|
||||
export type SetState<S> = (stateOrFn: S | ((state: S) => S)) => void;
|
||||
export type GetSnapshot<S> = () => S;
|
||||
export type Subscribe = (listener: () => void) => () => void;
|
||||
export type CreateStore = <T extends Record<string, unknown> | unknown[]>(
|
||||
initialState: T
|
||||
) => {
|
||||
getSnapshot: GetSnapshot<T>;
|
||||
setState: SetState<T>;
|
||||
subscribe: Subscribe;
|
||||
};
|
||||
|
||||
export const createStore: CreateStore = <
|
||||
T extends Record<string, unknown> | unknown[]
|
||||
>(
|
||||
initialState: T
|
||||
) => {
|
||||
let state = initialState;
|
||||
|
||||
const listeners = new Set<() => void>();
|
||||
const getSnapshot = () => state;
|
||||
const setState: SetState<T> = (stateOrFn) => {
|
||||
state = typeof stateOrFn === 'function' ? stateOrFn(state) : stateOrFn;
|
||||
listeners.forEach((listener) => listener());
|
||||
};
|
||||
const subscribe = (listener: () => void) => {
|
||||
listeners.add(listener);
|
||||
|
||||
return () => {
|
||||
listeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
getSnapshot,
|
||||
setState,
|
||||
subscribe,
|
||||
};
|
||||
};
|
||||
|
||||
export type Todo = {
|
||||
id: number;
|
||||
content: string;
|
||||
}[];
|
||||
const initialTodo: Todo = [
|
||||
{ id: 0, content: 'React' },
|
||||
{ id: 1, content: 'Vue' },
|
||||
];
|
||||
const todoStore = createStore(initialTodo);
|
||||
export const useTodoStore = (): [Todo, SetState<Todo>] => [
|
||||
useSyncExternalStore(todoStore.subscribe, todoStore.getSnapshot),
|
||||
todoStore.setState,
|
||||
];
|
||||
|
||||
type Count = {
|
||||
count: number;
|
||||
info: string;
|
||||
};
|
||||
const initialCount: Count = {
|
||||
count: 0,
|
||||
info: 'Hello',
|
||||
};
|
||||
const countStore = createStore(initialCount);
|
||||
export const useCountStore = (): [Count, SetState<Count>] => [
|
||||
useSyncExternalStore(countStore.subscribe, countStore.getSnapshot),
|
||||
countStore.setState,
|
||||
];`;
|
||||
|
||||
export const miniRedux = `import { useSyncExternalStore } from 'react';
|
||||
|
||||
export type RUAState = Record<string, unknown> | unknown[];
|
||||
export type RUAAction<P = unknown, T extends string = string> = {
|
||||
payload: P;
|
||||
type: T;
|
||||
};
|
||||
export type RUAReducer<S extends RUAState, A extends RUAAction> = (
|
||||
state: S,
|
||||
action: A
|
||||
) => S;
|
||||
export type RUADispatch<A extends RUAAction> = (action: A) => void;
|
||||
export type GetSnapshot<S> = () => S;
|
||||
export type Subscribe = (listener: () => void) => () => void;
|
||||
|
||||
export const createStore = <S extends RUAState, A extends RUAAction>(
|
||||
reducer: RUAReducer<S, A>,
|
||||
initialState: S
|
||||
) => {
|
||||
let state = initialState;
|
||||
|
||||
const listeners = new Set<() => void>();
|
||||
const getSnapshot = () => state;
|
||||
const dispatch: RUADispatch<A> = (action) => {
|
||||
state = reducer(state, action);
|
||||
listeners.forEach((listener) => listener());
|
||||
};
|
||||
const subscribe = (listener: () => void) => {
|
||||
listeners.add(listener);
|
||||
return () => {
|
||||
listeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
getSnapshot,
|
||||
dispatch,
|
||||
};
|
||||
};
|
||||
|
||||
export type Todo = {
|
||||
id: number;
|
||||
content: string;
|
||||
}[];
|
||||
const initialTodo: Todo = [
|
||||
{ id: 0, content: 'React' },
|
||||
{ id: 1, content: 'Vue' },
|
||||
];
|
||||
export type TodoAction = RUAAction<number | string, 'add' | 'delete'>;
|
||||
|
||||
const reducer: RUAReducer<Todo, TodoAction> = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'add': {
|
||||
if (action.payload == null) throw new Error('Add todo without payload!');
|
||||
return [
|
||||
...state,
|
||||
{
|
||||
id: state[state.length - 1].id + 1,
|
||||
content: action.payload.toString(),
|
||||
},
|
||||
];
|
||||
}
|
||||
case 'delete': {
|
||||
if (action.payload == null)
|
||||
throw new Error('Delete todo without payload!');
|
||||
return state.filter((todo) => todo.id !== action.payload);
|
||||
}
|
||||
default:
|
||||
throw new Error('Dispatch a reducer without action!');
|
||||
}
|
||||
};
|
||||
|
||||
const todoStore = createStore(reducer, initialTodo);
|
||||
export const useTodoStore = (): [Todo, RUADispatch<TodoAction>] => [
|
||||
useSyncExternalStore(todoStore.subscribe, todoStore.getSnapshot),
|
||||
todoStore.dispatch,
|
||||
];`;
|
||||
|
||||
export const MultiStore = `import Button from './Button';
|
||||
import Input from './Input';
|
||||
import { useState } from 'react';
|
||||
import { useCountStore, useTodoStore } from './multi';
|
||||
|
||||
const Todo = () => {
|
||||
const [todos, setTodo] = useTodoStore();
|
||||
const [value, setValue] = useState('');
|
||||
const handleAdd = () => {
|
||||
if (!value) return;
|
||||
setTodo((d) => [...d, { id: d[d.length - 1].id + 1, content: value }]);
|
||||
setValue('');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<ul>
|
||||
{todos.map((todo) => (
|
||||
<div key={todo.id}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
className="flex items-center mb-2"
|
||||
>
|
||||
<li className="mr-2">{todo.content}</li>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setTodo((d) => d.filter((item) => item.id !== todo.id))
|
||||
}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="text"
|
||||
className="mr-1"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
<Button onClick={handleAdd}>Add</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Count = () => {
|
||||
const [{ count, info }, setState] = useCountStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div>
|
||||
Count: <span>{count}</span>
|
||||
</div>
|
||||
<div>
|
||||
Info: <span>{info}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => setState((d) => ({ ...d, count: d.count + 1 }))}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Input
|
||||
type="text"
|
||||
onChange={(e) => setState((d) => ({ ...d, info: e.target.value }))}
|
||||
value={info}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const MultiStore = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="p-4">
|
||||
<Todo />
|
||||
<hr className="my-4" />
|
||||
<Count />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MultiStore;`;
|
||||
|
||||
export const Reducer = `import Button from './Button';
|
||||
import Input from './Input';
|
||||
import { useState } from 'react';
|
||||
import { useTodoStore } from './store.ts';
|
||||
|
||||
const Reducer = () => {
|
||||
const [todos, dispatch] = useTodoStore();
|
||||
const [value, setValue] = useState('');
|
||||
const handleAdd = () => {
|
||||
if (!value) return;
|
||||
dispatch({
|
||||
type: 'add',
|
||||
payload: value,
|
||||
});
|
||||
setValue('');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="p-4">
|
||||
<div>
|
||||
<ul>
|
||||
{todos.map((todo) => (
|
||||
<div key={todo.id}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
className="flex items-center mb-2"
|
||||
>
|
||||
<li className="mr-2">{todo.content}</li>
|
||||
<Button
|
||||
onClick={() => dispatch({ type: 'delete', payload: todo.id })}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="text"
|
||||
className="mr-1"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
<Button onClick={handleAdd}>Add</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Reducer;`;
|
@ -0,0 +1,90 @@
|
||||
export const genericApp = `import "./styles.css";
|
||||
import Child from "./Child";
|
||||
|
||||
const testData = {
|
||||
name: "xfy",
|
||||
age: 18
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<h1>Hello CodeSandbox</h1>
|
||||
<h2>Start editing to see some magic happen!</h2>
|
||||
|
||||
<div>
|
||||
<Child data={testData} name="name" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
export const genericChild = `import { useState } from "react";
|
||||
|
||||
type Props<T extends Record<string, unknown>> = {
|
||||
name: keyof T;
|
||||
data: T;
|
||||
};
|
||||
|
||||
const Child = <T extends Record<string, unknown>>({ name, data }: Props<T>) => {
|
||||
const [showName, setShowName] = useState<T[keyof T]>();
|
||||
const valid = () => {
|
||||
console.log(data[name]);
|
||||
setShowName(data[name]);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{name}</div>
|
||||
<button onClick={valid}>Show {name}</button>
|
||||
|
||||
<div>{JSON.stringify(showName)}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Child;`;
|
||||
|
||||
export const hookApp = `import { useForm } from 'react-hook-form';
|
||||
|
||||
type Pet = 'Cat' | 'Dog';
|
||||
type FormData = {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
favorite: Pet;
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm<FormData>();
|
||||
const onSubmit = handleSubmit((data) => console.log(data));
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={onSubmit}>
|
||||
<div>
|
||||
<label htmlFor="firstname">First name:</label>
|
||||
<input type="text" id="firstname" {...register('firstName')} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="lastname">Last name:</label>
|
||||
<input type="text" id="lastname" {...register('lastName')} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="favorite">Favorite pet:</label>
|
||||
<select id="favorite" {...register('favorite')}>
|
||||
<option value="cat">Cat</option>
|
||||
<option value="dog">Dog</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}`;
|
3
content/sandpack/hello-world.ts
Normal file
3
content/sandpack/hello-world.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const hello = `export default function App() {
|
||||
return <h1>Hello world</h1>
|
||||
}`;
|
116
content/sandpack/how-to-load-a-background-with-threejs.ts
Normal file
116
content/sandpack/how-to-load-a-background-with-threejs.ts
Normal file
@ -0,0 +1,116 @@
|
||||
export const firstScene = `import { useEffect, useRef } from 'react';
|
||||
import * as THREE from 'three';
|
||||
|
||||
export default function App() {
|
||||
const ref = useRef(null);
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas: ref.current,
|
||||
});
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
const render = (time) => {
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(render);
|
||||
};
|
||||
requestAnimationFrame(render);
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
render(0);
|
||||
}
|
||||
window.addEventListener('resize', onWindowResize);
|
||||
return () => {
|
||||
window.removeEventListener('resize', onWindowResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<canvas ref={ref}></canvas>
|
||||
</>
|
||||
)
|
||||
}`;
|
||||
|
||||
export const loadBackground = `import { useEffect, useRef } from "react";
|
||||
import * as THREE from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
||||
|
||||
const manager = new THREE.LoadingManager();
|
||||
manager.onProgress = (item, loaded, total) => {
|
||||
console.log(loaded, total);
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const scene = new THREE.Scene();
|
||||
const sky = new THREE.CubeTextureLoader(manager).load([
|
||||
"https://raw.githubusercontent.com/DefectingCat/three-playground/master/src/assets/images/corona/corona_ft.png",
|
||||
"https://raw.githubusercontent.com/DefectingCat/three-playground/master/src/assets/images/corona/corona_bk.png",
|
||||
"https://raw.githubusercontent.com/DefectingCat/three-playground/master/src/assets/images/corona/corona_up.png",
|
||||
"https://raw.githubusercontent.com/DefectingCat/three-playground/master/src/assets/images/corona/corona_dn.png",
|
||||
"https://raw.githubusercontent.com/DefectingCat/three-playground/master/src/assets/images/corona/corona_rt.png",
|
||||
"https://raw.githubusercontent.com/DefectingCat/three-playground/master/src/assets/images/corona/corona_lf.png"
|
||||
]);
|
||||
scene.background = sky;
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
camera.position.set(0, 1, 0);
|
||||
camera.up.set(0, 0, 1);
|
||||
scene.add(camera);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas: ref.current
|
||||
});
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
const controls = new OrbitControls(camera, ref.current);
|
||||
controls.enablePan = false;
|
||||
controls.target.set(0, 0, 0);
|
||||
controls.update();
|
||||
const render = (time) => {
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(render);
|
||||
};
|
||||
|
||||
requestAnimationFrame(render);
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
render(0);
|
||||
}
|
||||
window.addEventListener("resize", onWindowResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", onWindowResize);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<canvas ref={ref}></canvas>
|
||||
</>
|
||||
);
|
||||
}
|
||||
`;
|
||||
|
||||
export const resetStyles = `* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}`;
|
208
content/sandpack/index.ts
Normal file
208
content/sandpack/index.ts
Normal file
@ -0,0 +1,208 @@
|
||||
export const Button = `type ButtonProps = {} & React.DetailedHTMLProps<
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
HTMLButtonElement
|
||||
>;
|
||||
|
||||
const Button = ({ ...rest }: ButtonProps) => {
|
||||
const { children, className, ...props } = rest;
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className={\`rua-button \${className}\`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
{\`.rua-button {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
tab-size: 4;
|
||||
box-sizing: border-box;
|
||||
border-style: solid;
|
||||
border-color: #e5e7eb;
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
font-family: inherit;
|
||||
font-size: 100%;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
margin: 0;
|
||||
text-transform: none;
|
||||
-webkit-appearance: button;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
cursor: pointer;
|
||||
margin-right: 1rem;
|
||||
border-radius: 0.375rem;
|
||||
border-width: 1px;
|
||||
padding-left: 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.rua-button:focus {
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
\`}
|
||||
</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;`;
|
||||
|
||||
export const Input = `type InputProps = {} & React.DetailedHTMLProps<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
HTMLInputElement
|
||||
>;
|
||||
|
||||
const Input = ({ ...rest }: InputProps) => {
|
||||
const { className, ...props } = rest;
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
className={\`rua-input \${className}\`}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
<style>{\`
|
||||
.rua-input {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
tab-size: 4;
|
||||
box-sizing: border-box;
|
||||
border-style: solid;
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
font-family: inherit;
|
||||
font-size: 100%;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
margin-top: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
border-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(55 65 81 / var(--tw-text-opacity));
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.rua-input:focus {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(96 165 250 / var(--tw-border-opacity));
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
--tw-ring-color: rgb(147 197 253 / var(--tw-ring-opacity));
|
||||
--tw-ring-opacity: 0.8;
|
||||
}\`}
|
||||
</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Input;`;
|
258
content/sandpack/react18-new-hooks.ts
Normal file
258
content/sandpack/react18-new-hooks.ts
Normal file
@ -0,0 +1,258 @@
|
||||
export const useTransition = `import Button from './Button';
|
||||
import { useState, useTransition } from 'react';
|
||||
|
||||
const UseTransition = () => {
|
||||
const [value, setValue] = useState(0);
|
||||
const [value2, setValue2] = useState(-1);
|
||||
const [length, setLength] = useState(30000);
|
||||
const [pending, setTransiton] = useTransition();
|
||||
|
||||
const handleClick = () => {
|
||||
setValue((v) => v + 1);
|
||||
setTransiton(() => setLength((l) => l + 1));
|
||||
// setLength((l) => l + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-4">
|
||||
<Button onClick={handleClick}>{value}</Button>
|
||||
<Button onClick={() => setValue2((v) => v - 1)}>{value2}</Button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={\`wrapper \${pending && \`fade\`}\`}
|
||||
>
|
||||
{Array.from({ length }).map((_, i) => (
|
||||
<div className="p-2 mb-2 mr-2 rounded-md shadow" key={length - i}>
|
||||
{length - i}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<style>{\`
|
||||
.wrapper {
|
||||
transition: all 0.3 ease;
|
||||
}
|
||||
|
||||
.fade {
|
||||
opacity: 0.5;
|
||||
}\`}
|
||||
</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UseTransition;`;
|
||||
|
||||
export const useDeferredValue = `import Button from './Button';
|
||||
import { useDeferredValue, useState } from 'react';
|
||||
|
||||
const UseDeferredValue = () => {
|
||||
const [value, setValue] = useState(0);
|
||||
const deferred = useDeferredValue(value);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-4">
|
||||
<div>
|
||||
Deferred:
|
||||
<Button onClick={() => setValue((v) => v + 1)}>{deferred}</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Primtive:
|
||||
<Button onClick={() => setValue((v) => v + 1)}>{value}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{Array.from({ length: 30000 }).map((_, i) => (
|
||||
<div className="p-2 mb-2 mr-2 rounded-md shadow" key={i}>
|
||||
{i}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UseDeferredValue;`;
|
||||
|
||||
export const useId = `import Input from './Input';
|
||||
import { useId } from 'react';
|
||||
|
||||
const RUAForm = () => {
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<label htmlFor={\`\${id}\`}>Label 1: </label>
|
||||
<div>
|
||||
<Input type="text" id={\`\${id}\`} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const UseId = () => {
|
||||
return (
|
||||
<>
|
||||
<RUAForm />
|
||||
<RUAForm />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UseId;`;
|
||||
|
||||
export const store = `export type State = {
|
||||
count: number;
|
||||
info: string;
|
||||
};
|
||||
export type Store = {
|
||||
state: State;
|
||||
setState: (
|
||||
stateOrFn: State | ((state: State) => State)
|
||||
) => void;
|
||||
subscribe: (listener: () => void) => () => void;
|
||||
listeners: Set<() => void>;
|
||||
getSnapshot: () => State;
|
||||
};
|
||||
|
||||
const store: Store = {
|
||||
state: {
|
||||
count: 0,
|
||||
info: 'Hello',
|
||||
},
|
||||
setState(stateOrFn) {
|
||||
const newState =
|
||||
typeof stateOrFn === 'function' ? stateOrFn(store.state) : stateOrFn;
|
||||
store.state = {
|
||||
...store.state,
|
||||
...newState,
|
||||
};
|
||||
store.listeners.forEach((listener) => listener());
|
||||
},
|
||||
listeners: new Set(),
|
||||
subscribe(listener) {
|
||||
store.listeners.add(listener);
|
||||
return () => {
|
||||
store.listeners.delete(listener);
|
||||
};
|
||||
},
|
||||
getSnapshot() {
|
||||
return store.state;
|
||||
},
|
||||
};
|
||||
|
||||
export default store;`;
|
||||
export const useSyncExternalStore = `import Button from './Button';
|
||||
import Input from './Input';
|
||||
import { useSyncExternalStore } from 'react';
|
||||
import store from './store';
|
||||
|
||||
const Couter = () => {
|
||||
const { count, info } = useSyncExternalStore(
|
||||
store.subscribe,
|
||||
store.getSnapshot
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div>
|
||||
Count: <span>{count}</span>
|
||||
</div>
|
||||
<div>
|
||||
Info: <span>{info}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => store.setState((d) => ({ ...d, count: d.count + 1 }))}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Infor = () => {
|
||||
const { count, info } = useSyncExternalStore(
|
||||
store.subscribe,
|
||||
store.getSnapshot
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div>
|
||||
Count: <span>{count}</span>
|
||||
</div>
|
||||
<div>
|
||||
Info: <span>{info}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="text"
|
||||
onChange={(e) => store.setState((d) => ({ ...d, info: e.target.value }))}
|
||||
value={info}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const UseSyncExternalStore = () => {
|
||||
return (
|
||||
<>
|
||||
<Couter />
|
||||
<hr className="my-4" />
|
||||
<Infor />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UseSyncExternalStore;`;
|
||||
|
||||
export const useInsertionEffect = `import { useEffect, useLayoutEffect, useInsertionEffect } from 'react';
|
||||
|
||||
const Child = () => {
|
||||
useEffect(() => {
|
||||
console.log('useEffect child is called');
|
||||
});
|
||||
useLayoutEffect(() => {
|
||||
console.log('useLayoutEffect child is called');
|
||||
});
|
||||
useInsertionEffect(() => {
|
||||
console.log('useInsertionEffect child is called');
|
||||
});
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const UseInsertionEffect = () => {
|
||||
useEffect(() => {
|
||||
console.log('useEffect app is called');
|
||||
});
|
||||
useLayoutEffect(() => {
|
||||
console.log('useLayoutEffect app is called');
|
||||
});
|
||||
useInsertionEffect(() => {
|
||||
console.log('useInsertionEffect app is called');
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Child />
|
||||
<div>Check console in DevTools</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UseInsertionEffect;`;
|
Reference in New Issue
Block a user