Interface vs Type
When you’re not sure which one to use, always go with interface until you have a reason to use type.
type isLoading = boolean
type Theme = "dark" | "light"
type Lang = "en" | "fr"Function Components
interface AppProps {
title: string
children: ReactNode
}
// easiest way
const App = ({ title, children }: AppProps) => { ... }
// not recommended
const App: FC<AppProps> = ({ title, children }) => { ... }
// when there are a few props
const App = ({ message }: { message: string }) => { ... }Custom HTML Components
type InputProps = ComponentProps<'input'>
const Input = forwardRef(
(props: InputProps, ref: Ref<HTMLInputElement>) => {
return <input ref={ref} {...props} className={...} />
}
)Use text value instead of children:
type TagProps = {
variant?: "solid" | "outlined"
text: string
} & Omit<React.ComponentProps<"span">, "children">useState
// infer types
const [enabled, setEnabled] = useState(false)
const [title, setTitle] = useState<string | null>(null)
type Status = "idle" | "loading" | "success" | "error"
const [status, setStatus] = useState<Status>("idle")useReducer
const initialTasks: TaskItem[] = []
enum ACTIONS {
ADD = "added",
CHANGE = "changed",
DELETE = "deleted",
}
type ActionType =
| { type: ACTIONS.ADD; id: string; text: string }
| { type: ACTIONS.CHANGE; task: TaskItem }
| { type: ACTIONS.DELETE; id: string }
function tasksReducer(tasks: typeof initialTasks, action: ActionType) {
switch (action.type) {
case ACTIONS.ADD: {
return [...tasks, { id: action.id, text: action.text, done: false }]
}
case ACTIONS.CHANGE: {
return tasks.map((t) => {
if (t.id === action.task.id) {
return action.task
} else {
return t
}
})
}
case ACTIONS.DELETE: {
return tasks.filter((t) => t.id !== action.id)
}
default: {
throw Error("Unknown Type")
}
}
}
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks)useContext
const TasksContext = createContext<TaskItem[]>([])
const TasksDispatchContext = createContext<Dispatch<ActionType>>(() => {})
export function useTasks() {
return useContext(TasksContext)
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext)
}
export function TaskProvider({ children }: { children: ReactNode }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks)
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>{children}</TasksDispatchContext.Provider>
</TasksContext.Provider>
)
}