Skip to content

Commit b32ecc3

Browse files
committed
add react hooks
1 parent 12b0742 commit b32ecc3

File tree

4 files changed

+357
-4
lines changed

4 files changed

+357
-4
lines changed

docs.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"image-embeds",
4949
"list-table",
5050
"code",
51-
"reusable-snippets"
51+
"reusable-snippets",
52+
"react-hooks"
5253
]
5354
},
5455
{
@@ -203,9 +204,7 @@
203204
},
204205
{
205206
"group": "Custom Components",
206-
"pages": [
207-
"content/components/reusable-components"
208-
]
207+
"pages": ["content/components/reusable-components"]
209208
},
210209
{
211210
"group": "API Components",

react-hooks.mdx

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
---
2+
title: "React Hooks"
3+
description: "Learn how to use React hooks"
4+
icon: "react"
5+
---
6+
7+
8+
import { Counter } from "/snippets/counter.mdx";
9+
import { ColorGenerator } from "/snippets/color-generator.mdx";
10+
11+
Create interactive examples and dynamic content in your documentation using familiar [React hooks](https://react.dev/reference/react/hooks).
12+
13+
<Note>
14+
No imports needed! All standard React hooks are automatically available in your MDX files.
15+
</Note>
16+
17+
## Available Hooks
18+
19+
You can use all standard [React hooks](https://react.dev/reference/react/hooks) in your documentation, such as `useState`, `useEffect`, etc.
20+
21+
## Examples
22+
23+
### 1. Directly in your MDX file
24+
create a component directly in your MDX file. Use `useState` to create interactive components that respond to user actions:
25+
26+
<Counter />
27+
28+
```jsx file.mdx
29+
export const Counter = () => {
30+
const [count, setCount] = useState(0);
31+
32+
return (
33+
<div>
34+
<p>Current count: {count}</p>
35+
<button onClick={() => setCount(count + 1)}>
36+
+
37+
</button>
38+
</div>
39+
);
40+
}
41+
42+
<Counter />
43+
```
44+
45+
46+
### 2. Import from a snippet file
47+
48+
Your snippets can be imported into your MDX files to create interactive components. Snippets should be stored in the `snippets` folder, check out the [snippets](/reusable-snippets) page for more information.
49+
50+
51+
Here's an example of a snippet file that creates a color generator component:
52+
53+
<ColorGenerator />
54+
55+
<Steps>
56+
<Step title="Create a snippet file">
57+
Create a snippet file in the `snippets` folder.
58+
59+
```jsx /snippets/color-generator.mdx [expandable]
60+
export const ColorGenerator = () => {
61+
const [hue, setHue] = useState(180)
62+
const [saturation, setSaturation] = useState(50)
63+
const [lightness, setLightness] = useState(50)
64+
const [colors, setColors] = useState([])
65+
66+
useEffect(() => {
67+
const newColors = []
68+
for (let i = 0; i < 5; i++) {
69+
const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
70+
newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
71+
}
72+
setColors(newColors)
73+
}, [hue, saturation, lightness])
74+
75+
const copyToClipboard = (color) => {
76+
navigator.clipboard
77+
.writeText(color)
78+
.then(() => {
79+
console.log(`Copied ${color} to clipboard!`)
80+
})
81+
.catch((err) => {
82+
console.error("Failed to copy: ", err)
83+
})
84+
}
85+
86+
return (
87+
<div className="p-4 border dark:border-zinc-800 rounded-xl bg-white dark:bg-gray-800 shadow-sm">
88+
<span className="text-xl mb-4 font-semibold text-gray-800 dark:text-white">HSL Color Generator</span>
89+
90+
<div className="space-y-4">
91+
<div className="space-y-2">
92+
<label className="block text-sm text-gray-700 dark:text-gray-300">
93+
Hue: {hue}°
94+
<input
95+
type="range"
96+
min="0"
97+
max="360"
98+
value={hue}
99+
onChange={(e) => setHue(Number.parseInt(e.target.value))}
100+
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
101+
style={{
102+
background: `linear-gradient(to right,
103+
hsl(0, ${saturation}%, ${lightness}%),
104+
hsl(60, ${saturation}%, ${lightness}%),
105+
hsl(120, ${saturation}%, ${lightness}%),
106+
hsl(180, ${saturation}%, ${lightness}%),
107+
hsl(240, ${saturation}%, ${lightness}%),
108+
hsl(300, ${saturation}%, ${lightness}%),
109+
hsl(360, ${saturation}%, ${lightness}%))`,
110+
}}
111+
/>
112+
</label>
113+
114+
<label className="block text-sm text-gray-700 dark:text-gray-300">
115+
Saturation: {saturation}%
116+
<input
117+
type="range"
118+
min="0"
119+
max="100"
120+
value={saturation}
121+
onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
122+
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
123+
style={{
124+
background: `linear-gradient(to right,
125+
hsl(${hue}, 0%, ${lightness}%),
126+
hsl(${hue}, 50%, ${lightness}%),
127+
hsl(${hue}, 100%, ${lightness}%))`,
128+
}}
129+
/>
130+
</label>
131+
132+
<label className="block text-sm text-gray-700 dark:text-gray-300">
133+
Lightness: {lightness}%
134+
<input
135+
type="range"
136+
min="0"
137+
max="100"
138+
value={lightness}
139+
onChange={(e) => setLightness(Number.parseInt(e.target.value))}
140+
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
141+
style={{
142+
background: `linear-gradient(to right,
143+
hsl(${hue}, ${saturation}%, 0%),
144+
hsl(${hue}, ${saturation}%, 50%),
145+
hsl(${hue}, ${saturation}%, 100%))`,
146+
}}
147+
/>
148+
</label>
149+
</div>
150+
151+
<div className="flex space-x-1">
152+
{colors.map((color, idx) => (
153+
<div
154+
key={idx}
155+
className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
156+
style={{ backgroundColor: color }}
157+
title={`Click to copy: ${color}`}
158+
onClick={() => copyToClipboard(color)}
159+
/>
160+
))}
161+
</div>
162+
163+
<div className="text-sm font-mono text-gray-700 dark:text-gray-300">
164+
<p>
165+
Base color: hsl({hue}, {saturation}%, {lightness}%)
166+
</p>
167+
</div>
168+
</div>
169+
</div>
170+
)
171+
}
172+
173+
```
174+
</Step>
175+
<Step title="Import the snippet">
176+
On the top of your MDX file, import the snippet:
177+
178+
```jsx file.mdx
179+
import { ColorGenerator } from "/snippets/color-generator.mdx"
180+
```
181+
</Step>
182+
<Step title="Use the component">
183+
<p>Use the component in your MDX file.</p>
184+
185+
```jsx file.mdx
186+
<ColorGenerator />
187+
```
188+
</Step>
189+
</Steps>
190+
191+
192+
## Important Considerations
193+
194+
<AccordionGroup>
195+
<Accordion title="Client-Side Rendering Impact">
196+
React hook components are client-side rendered, which has several implications:
197+
198+
- **SEO**: Search engines might not index dynamic content properly
199+
- **Initial Load**: Users might see a flash of loading content before the components render
200+
</Accordion>
201+
202+
<Accordion title="Performance Best Practices">
203+
- **Optimize Dependency Arrays**: Include only necessary dependencies in your `useEffect` dependency arrays
204+
- **Avoid Complex Calculations**: Move expensive operations to `useMemo` or `useCallback`
205+
- **Control Re-renders**: Break large components into smaller ones to prevent unnecessary re-renders
206+
</Accordion>
207+
</AccordionGroup>
208+
209+
210+

snippets/color-generator.mdx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
export const ColorGenerator = () => {
2+
const [hue, setHue] = useState(165)
3+
const [saturation, setSaturation] = useState(84)
4+
const [lightness, setLightness] = useState(31)
5+
const [colors, setColors] = useState([])
6+
7+
useEffect(() => {
8+
const newColors = []
9+
for (let i = 0; i < 5; i++) {
10+
const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
11+
newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
12+
}
13+
setColors(newColors)
14+
}, [hue, saturation, lightness])
15+
16+
const copyToClipboard = (color) => {
17+
navigator.clipboard
18+
.writeText(color)
19+
.then(() => {
20+
console.log(`Copied ${color} to clipboard!`)
21+
})
22+
.catch((err) => {
23+
console.error("Failed to copy: ", err)
24+
})
25+
}
26+
27+
return (
28+
<div className="p-4 border dark:border-zinc-800 rounded-xl bg-white dark:bg-gray-800 shadow-sm not-prose">
29+
<p className="text-xl mb-4 font-semibold text-gray-800 dark:text-white">HSL Color Generator</p>
30+
31+
<div className="space-y-4">
32+
<div className="space-y-2">
33+
<label className="block text-sm text-gray-700 dark:text-gray-300">
34+
Hue: {hue}°
35+
<input
36+
type="range"
37+
min="0"
38+
max="360"
39+
value={hue}
40+
onChange={(e) => setHue(Number.parseInt(e.target.value))}
41+
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
42+
style={{
43+
background: `linear-gradient(to right,
44+
hsl(0, ${saturation}%, ${lightness}%),
45+
hsl(60, ${saturation}%, ${lightness}%),
46+
hsl(120, ${saturation}%, ${lightness}%),
47+
hsl(180, ${saturation}%, ${lightness}%),
48+
hsl(240, ${saturation}%, ${lightness}%),
49+
hsl(300, ${saturation}%, ${lightness}%),
50+
hsl(360, ${saturation}%, ${lightness}%))`,
51+
}}
52+
/>
53+
</label>
54+
55+
<label className="block text-sm text-gray-700 dark:text-gray-300">
56+
Saturation: {saturation}%
57+
<input
58+
type="range"
59+
min="0"
60+
max="100"
61+
value={saturation}
62+
onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
63+
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
64+
style={{
65+
background: `linear-gradient(to right,
66+
hsl(${hue}, 0%, ${lightness}%),
67+
hsl(${hue}, 50%, ${lightness}%),
68+
hsl(${hue}, 100%, ${lightness}%))`,
69+
}}
70+
/>
71+
</label>
72+
73+
<label className="block text-sm text-gray-700 dark:text-gray-300">
74+
Lightness: {lightness}%
75+
<input
76+
type="range"
77+
min="0"
78+
max="100"
79+
value={lightness}
80+
onChange={(e) => setLightness(Number.parseInt(e.target.value))}
81+
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 mt-1"
82+
style={{
83+
background: `linear-gradient(to right,
84+
hsl(${hue}, ${saturation}%, 0%),
85+
hsl(${hue}, ${saturation}%, 50%),
86+
hsl(${hue}, ${saturation}%, 100%))`,
87+
}}
88+
/>
89+
</label>
90+
</div>
91+
92+
<div className="flex space-x-2">
93+
{colors.map((color, idx) => (
94+
<div
95+
key={idx}
96+
className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
97+
style={{ backgroundColor: color }}
98+
title={`Click to copy: ${color}`}
99+
onClick={() => copyToClipboard(color)}
100+
/>
101+
))}
102+
</div>
103+
104+
<div className="text-sm font-mono text-gray-700 dark:text-gray-300">
105+
<p>
106+
Base color: hsl({hue}, {saturation}%, {lightness}%)
107+
</p>
108+
</div>
109+
</div>
110+
</div>
111+
)
112+
}

snippets/counter.mdx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export const Counter = () => {
2+
const [count, setCount] = useState(0)
3+
4+
const increment = () => setCount(count + 1)
5+
const decrement = () => setCount(count - 1)
6+
7+
return (
8+
<div className="flex items-center justify-center">
9+
<div className="flex items-center rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 shadow-sm">
10+
<button
11+
onClick={decrement}
12+
className="flex items-center justify-center h-8 w-8 bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-900 dark:hover:bg-gray-800 dark:text-gray-200 transition-colors border-r border-gray-200 dark:border-gray-700"
13+
aria-label="Decrease"
14+
>
15+
-
16+
</button>
17+
18+
<div className="flex text-sm items-center justify-center h-8 px-6 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 font-medium min-w-[4rem] text-center">
19+
{count}
20+
</div>
21+
22+
<button
23+
onClick={increment}
24+
className="flex items-center justify-center h-8 w-8 bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-900 dark:hover:bg-gray-800 dark:text-gray-200 transition-colors border-l border-gray-200 dark:border-gray-700"
25+
aria-label="Increase"
26+
>
27+
+
28+
</button>
29+
</div>
30+
</div>
31+
)
32+
}

0 commit comments

Comments
 (0)