React 筆記
記錄一下常用到的 Components
JSX
shadcn
相見恨晚阿.. 太強了
官方文件
TanStack
官方有個非常詳盡的文件,這裡只有寫大概怎麼用。
文件寫到 TanStack Table is a modular library. Not all code for every single feature is included in the createTable functions/hooks by default.
,所以很多功能要在建立 table 的時候自己 import。
安裝
1
npm install @tanstack/react-table
Header
Header Group 包含所有 header row
1 |
|
Column
可以從 cell
、 header
和 table
取得
1 |
|
Row
全部可以用的 row model
1 |
|
table.getRow()
1 |
|
table.getRowModel()
1 |
|
table.getSelectedRowModel()
1 |
|
row.getValue()
、row.renderValue()
兩個只差在檢查 value 是否為 undefined
1 |
|
row.original
跟 row.getValue()
類似,但是回傳 accessorFn
處理前 的值,是最原始的資料
1 |
|
Cell
每個 Cell 的 id 是由 parent row 和 parent col 決定的:{ id:
${row.id}_${column.id} }
cell.getValue()
、cell.renderValue()
1 |
|
取得 parent row
1 |
|
flexRender()
如果 column.cell
是 函數回傳 JSX 而不是純數據,就一定要用 flexRender
渲染,不能用 getValue()
1 |
|
getVisibleCells()
只回傳當前可見的 Cells,避免 render 不需要的內容
1 |
|
React Hook Form
透過 useForm hook
管理表單
安裝
1
npm install react-hook-form
shadui
+react hook form
+zod
驗證範例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
const formSchema = z.object({
name: z.string().min(2, "名稱至少需要 2 個字"),
email: z.string().email("請輸入有效的 Email"),
agreeTerms: z.boolean().refine(val => val === true, {
message: "您必須同意條款與條件"
})
});
export default function MyForm() {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
email: "",
agreeTerms: false
}
});
const onSubmit = (data) => {
console.log("Submitted data:", data);
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4 p-4 border rounded-lg w-full max-w-md">
{/* Name Field */}
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>名稱</FormLabel>
<FormControl>
<Input placeholder="輸入您的名稱" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Email Field */}
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="輸入您的 Email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Agree Terms Checkbox */}
<FormField
control={form.control}
name="agreeTerms"
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<Checkbox checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel>我同意條款與條件</FormLabel>
<FormMessage />
</FormItem>
)}
/>
{/* Submit Button */}
<Button type="submit" className="w-full">提交</Button>
</form>
</Form>
);
}
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
const formSchema = z.object({
name: z.string().min(2, "名稱至少需要 2 個字"),
email: z.string().email("請輸入有效的 Email"),
agreeTerms: z.boolean().refine(val => val === true, {
message: "您必須同意條款與條件"
})
});
export default function MyForm() {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
email: "",
agreeTerms: false
}
});
const onSubmit = (data) => {
console.log("Submitted data:", data);
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4 p-4 border rounded-lg w-full max-w-md">
{/* Name Field */}
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>名稱</FormLabel>
<FormControl>
<Input placeholder="輸入您的名稱" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Email Field */}
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="輸入您的 Email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Agree Terms Checkbox */}
<FormField
control={form.control}
name="agreeTerms"
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<Checkbox checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
<FormLabel>我同意條款與條件</FormLabel>
<FormMessage />
</FormItem>
)}
/>
{/* Submit Button */}
<Button type="submit" className="w-full">提交</Button>
</form>
</Form>
);
}
Swiper
安裝
1
npm install swiper
import
一定要的
1
2import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";其他一些可用的 modules
1
2import { EffectCoverflow, Pagination } from "swiper/modules";
import "swiper/css/pagination";
用法範例
- Swiper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<Swiper
onSlideChange={(swiper) => setcurrentAppIntroIndex(swiper.activeIndex)}
initialSlide={2}
slidesPerView={"auto"}
coverflowEffect={{
rotate: 0,
stretch: 80,
depth: 300,
modifier: 1,
slideShadows: true,
}}
effect={"coverflow"}
grabCursor={true}
centeredSlides={true}
onSwiper={(swiper) => (appSwiperRef.current = swiper)}
pagination={{ el: ".app-pagination", clickable: true }}
modules={[EffectCoverflow, Pagination]}
>
{appFeatures.map((item, index) => (
<SwiperSlide key={index}>
<div>{item.title}</div>
</SwiperSlide>
))}
</Swiper> - Pagination
1
2
3
4
5
6
7
8
9<div
className="app-pagination mt-2 flex justify-center space-x-4"
style={{
"--swiper-pagination-color": "#000000",
"--swiper-pagination-bullet-inactive-color": "#999999",
"--swiper-pagination-bullet-inactive-opacity": "1",
"--swiper-pagination-bullet-size": "8px",
}}
/>
- Swiper
Collapse
安裝
1
npm install --save react-collapse
import
1
import { Collapse } from "react-collapse";
另外,
index.html
要加入1
2<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-collapse/build/react-collapse.min.js"></script>用法範例
用isOpened
控制 Toggle 就好,內容隨意1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<Collapse isOpened={isExpanded[index]}>
{question.description &&
question.description.map((desc, index) => (
<div>
<div key={index}>{desc.description}</div>
{desc.details && (
<ul className="ml-10 list-disc">
{desc.details.map((detail, index) => (
<li key={index}>
<span className="font-bold">{detail.title}</span>
{detail.description}
</li>
))}
</ul>
)}
</div>
))}
</Collapse>- Collapse 動畫的時間
1
2
3.ReactCollapse--collapse {
transition: height 500ms;
}
Select
安裝
1
npm i --save react-select
import
1
import Select from "react-select";
範例
1
2
3
4
5
6const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
];
const MyComponent = () => <Select options={options} />;
Spinner
安裝
1
npm install --save react-spinners
import
1
import ClipLoader from "react-spinners/ClipLoader";
範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<ClipLoader
color="#ffffff"
loading={loading} // bool 值
size={150}
aria-label="Loading Spinner"
data-testid="loader"
/>
<BeatLoader
color="#000000"
loading={true} // bool 值
size={10}
aria-label="Loading Spinner"
data-testid="loader"
/>
CSS
flexwrap
用 flex
的時候,會與下一行有一些奇怪的間隔,是因為他會啟動 aligin-content: strench
,要改成 align-content: flex-start
,或 Tailwind 的 content-start