diff --git a/Programming_Languages/React/React_Typescript/Built_in_hooks.md b/Programming_Languages/React/React_Typescript/Built_in_hooks.md new file mode 100644 index 0000000..c7ae4f2 --- /dev/null +++ b/Programming_Languages/React/React_Typescript/Built_in_hooks.md @@ -0,0 +1,99 @@ +--- +tags: + - Programming_Languages + - typescript + - react + - react-hooks +--- + +# Typing built-in hooks + +## `useState` + +```tsx +const [amount, setAmount] = useState(3); +``` + +### With a ustom type + +```tsx +interface IState { + people: IPerson[]; +} + +interface IPerson { + name: string; + age: number; +} + +const [people, setPeople] = useState({}); + +// Alternative declaration + +interface IState { + people: { + name: string; + age: number; + }[]; +} + +const [people, setPeople] = useState({}); +``` + +## `useReducer` + +Mostly the same. They key thing to type is the reducer function itself. You don't need to type the intitial state unless it is a pre-existing type, you can just use type assertion: + +```jsx +interface IAction { + | { type: "increment"; payload: number } + | { type: "decrement"; payload: string }; +} + +const initialState = { count: 0 }; + +function reducer(state: typeof initialState, action: IAction) { + switch (action.type) { + case "increment": + return { count: state.count + action.payload }; + case "decrement": + return { count: state.count - Number(action.payload) }; + default: + throw new Error(); + } +} +``` + +Then the syntax is as normal, i.e: + +```js +const [state, dispatch] = useReducer(reducer, initialState); +``` + +## `useEffect` + +> Limited potential for typing here as this hook does not return a value. See if there is useful inferred typing from use and update. + +## `useContext` + +We can use generics but mostly this is untyped: + +```jsx +type Theme = 'light' | 'dark'; +const ThemeContext = createContext < Theme > 'dark'; + +const App = () => ( + + + +); + +const MyComponent = () => { + const theme = useContext(ThemeContext); + return
The theme is {theme}
; +}; +``` + +## `useCallback` / `useMemo` + +You must only type the parameters that are passed to the callback, the return value will be inferred. diff --git a/Programming_Languages/React/React_Typescript/Components.md b/Programming_Languages/React/React_Typescript/Components.md new file mode 100644 index 0000000..80ae6a4 --- /dev/null +++ b/Programming_Languages/React/React_Typescript/Components.md @@ -0,0 +1,27 @@ +--- +tags: + - Programming_Languages + - typescript + - react +--- + +# Components + +We write functional components in the manner of a normal [function](/Programming_Languages/React/React_Typescript/Functions.md) that take a props argument and return a JSX element. + +```jsx +interface IProps = { + message: string; +}; +const App = ({message}: IProps): JSX.Element =>
{message}
; +``` + +## `React.FunctionComponent` / `React.FC` + +It used to be the case that you would type a function component like so: + +```jsx +const App: React.FunctionComponent<{message: string}> = ({message}) =>
{message}
; +``` + +This is now discouraged as it means the component can always accept children, even when you do not want this to be the case. There are several other minor drawerbacks but it's generally best to leave out the declaration. diff --git a/Programming_Languages/React/React_Typescript/Events.md b/Programming_Languages/React/React_Typescript/Events.md index 4507d8e..4ab2fb7 100644 --- a/Programming_Languages/React/React_Typescript/Events.md +++ b/Programming_Languages/React/React_Typescript/Events.md @@ -33,7 +33,7 @@ interface IProps { setPeople: React.Dispatch> } -const AddToList: React.FC = () => { +const AddToList = () => { const [people, setPeople] = useState({}) const [formVals, setFormVals] = useState({}); @@ -62,7 +62,7 @@ return ( -); + ); }; ``` @@ -71,3 +71,24 @@ This follows standard practise for [controlled-components](/Programming_Language - We define the change event as being of the type `React.ChangeEvent` and state that it corresponds to a generic - `HTMLInputElement`. So we are saying that whenever this function is called we must be passing it an input element so that we can extract the event associated with its `target` property. - We are passing around variations on the `IState` interface in order to type the values that we are adding to the people array. + +## Further standard types for event handling + +### onClick + +```tsx +handleClick(event: MouseEvent) { + event.preventDefault(); + alert(event.currentTarget.tagName); +} +``` + +### onSubmit + +```tsx +handleSubmit(e: React.SyntheticEvent) { + event.preventDefault(); +} +``` + +> Most event types and their associated generics will be revealed by VS Code Intellisense so you don't need to memorize them all diff --git a/Programming_Languages/React/React_Typescript/Managing_state.md b/Programming_Languages/React/React_Typescript/Managing_state.md deleted file mode 100644 index 7986dd2..0000000 --- a/Programming_Languages/React/React_Typescript/Managing_state.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -tags: - - Programming_Languages - - typescript - - react ---- - -# Managing state - -## Basic: `useState` - -```tsx -const [amount, setAmount] = useState(3); -``` - -### Custom type - -```tsx -interface IState { - people: IPerson[]; -} - -interface IPerson { - name: string; - age: number; -} - -const [people, setPeople] = useState({}); - -// Alternative declaration - -interface IState { - people: { - name: string; - age: number; - }[]; -} - -const [people, setPeople] = useState({}); -``` diff --git a/Programming_Languages/React/React_Typescript/Props.md b/Programming_Languages/React/React_Typescript/Props.md index 892f717..223abc1 100644 --- a/Programming_Languages/React/React_Typescript/Props.md +++ b/Programming_Languages/React/React_Typescript/Props.md @@ -7,6 +7,46 @@ tags: # Props +## Types or interfaces? + +They are mostly interchangeable but there are some specific differences + +### Interfaces + +- Classes and other interfaces can extend an interface but not a type + + ```ts + interface Animal { + name: string; + } + + interface Bear extends Animal { + honey: boolean; + } + ``` + +- You can add properties to an interface without generating a double declaration error: + + ```ts + interface Window { + title: string; + } + + interface Window { + ts: TypeScriptAPI; + } + ``` + +### Types + +You can create unions with types but not interfaces. + +> The consensus seems to be that interfaces should be used over types unless there are ocassions when you do not want a type to be extendable / redeclarable. Or when you want to use unions. + +## How we type props + +The custom is to type props as an interface. This way we can easily type function prop types. + ```tsx interface IProps { people: { @@ -35,4 +75,56 @@ const [people, setPeople] = useState({}); ``` +## Common types for props + +### Object with any number of properties + +```tsx +interface IProps { + obj: {}; +} +``` + +### Array of objects + +```tsx +interface IProps { + val1: string; + val2: boolean; +} +[]; +``` + +### Functions + +```tsx + +// Function that doesn't receive or return anything +interface IProps { + onClick: () => void; +}[] + +// Function with named parameter +interface IProps { + onClick: (id: number) => void; +}[] + +// Function that takes an event +onChange: (event: React.ChangeEvent) => void; +``` + +### Other React components + +```js +interface IProps { + children: React.ReactNode; +} + +interface IProps { + children: JSX.Element; +} +``` + +`React.ReactNode` covers everything that React can render. This should be definitely used when the prop is a child component, otherwise `JSX.element` is ok. +

Should I use type or interface? What is consensus?