Other resources
My other pages:
External resources:
What & Link | Type |
---|---|
"The React Handbook" by Flavio Copes - Online version PDF signup ---> Amazing resource! |
Guide |
"React for Vue Developers" by Sebastian De Deyne | Guide / Cheatsheet |
React Lifecycles Method Diagram | Cheatsheet |
React+TypeScript Cheatsheets - Live Website |
Cheatsheet |
React Redux TypeScript Guide | Cheatsheet |
PluralSight: Composing React Components with TypeScript | Guide |
Post: "You Probably Don't Need Derived State" | Guide |
Collection: enaqx/awesome-react | Collection |
Kent C. Dodds - "Super Simple Start to React" | Guide |
Steven Jin: Cheat Sheet for React Interview, 2020 | Refresher / Cheatsheet |
learning-zone/react-interview-questions | Refresher / Cheatsheet |
There are a few people in the React space that consistently have tons of really great and up-to-date content: Kent C. Dodds (website, GitHub), Josh W Comeau, and Shawn Wang (aka swyx). If you are looking for lots of React content to absorb, going through their material is a good start point.
Binding template literal
Binding template literal to property expecting string: You have to use double brackets to "break out" of JSX and into regular JS. Instead of:
<SEO title="`${props.pageContext.slug}`" />
You need:
<SEO title={`${props.pageContext.slug}`} />
Unique Keys - Creating and Using
For any generated list of elements, you want a unique identifier as a key
property (see docs), which is actually harder than one might expect to create. Using the index
provided within a loop is not the recommended solution for many situations, and even combining with something like (new Date()).getTime()
is not guaranteed to be unique.
There are tons of packages out there that you can drop in to create unique Ids. Or just make a composite key out of enough unique parameters to ensure non-duplicates.
Here are some general rules for the key
property and its value:
- The
key
prop should go on the highest level siblings generated from code that spits out an array of elements- If you are returning fragments from something like
map
, add thekey
to the fragment, not its child (or children). If you are using<>
as the shorthand, switch to<React.Fragment key={VALUE}>
.
- If you are returning fragments from something like
- The key should be stable between renders - meaning that if items move around in a list, React should be able to track them as they move
- This is why only using
index
as a value forkey
is not always a good move
- This is why only using
Misc / How Do I...
- You can now use empty elements, known as fragments, to wrap siblings:
<><div></div><div></div></>
- Issues returning JSX?
- More than one line? Make sure wrapped in parenthesis
return (<div></div>)
- Make sure there is only one root level element
- More than one line? Make sure wrapped in parenthesis
- Pass a bunch of options to a component as props (basically convert JS Obj to props)
- Just use spread operator -
{...props}
- You can use this re-use the same props across multiple components!
- Be careful about directly spreading vs capturing via rest parameters and then spreading - you might not want to pass all props to child component
- This is why you often see
const {myVarA, myVarB, ...restProps} = props;
inside components ->restProps
would be a subset ofprops
that does not containmyVarA
ormyVarB
- This is why you often see
- Just use spread operator -
- Simulate a change event on a
<input>
element- Use a combination of an exposed setter with a manual event dispatch
- See https://stackoverflow.com/a/46012210/11447682
- If the above doesn't work, you might need to hook into the
._valueTracker
property - try this or this.
- How to add an external stylesheet (not for bundling)
- Instead of using
import
within a JS file (like App.js), add the stylesheet directly as a link tag element in the HTML file (e.g.<link rel="stylesheet" href="https://.../style.css" />
)
- Instead of using
- How do I access an element directly (similar to native browser APIs like
document.getElementById
) in React? How do I get the DOM node?- That is a great use case for React
refs
! - Docs - Options:
- Newest and recommended:
useRef
hookmyRef = useRef(null)
- Older:-
this.myRef = React.createRef()
in class constructor
- Newest and recommended:
- You can access DOM node through
myRef.current
, after attaching via<MyElement ref={myRef}/>
- That is a great use case for React
- How do I expose and call a function from a child component in a parent component (aka passing methods up through the component tree)?
- This is considered a bit of an anti-pattern with React, and is generally avoided entirely (things are always passed down, not up). Alternative (arguably better) options:
- Use change in props passed to child as way to trigger method / update in child
- Use state in shared context
- However, there are still ways to do so - the main way is to use refs (S/O, Docs)
- Another option, if you need this to update a piece of state in the child component while letting the child still call setState, is to have the child component accept a state getter and setter tuple directly as a prop
- This is considered a bit of an anti-pattern with React, and is generally avoided entirely (things are always passed down, not up). Alternative (arguably better) options:
- What's with the
{}
curly braces? Is this Mustache or Handlebars templating?- No, the curly braces simply tell the JSX compiler that you are about to give it something that should be evaluated as a JavaScript Expression, rather than a string or component.
- How do I conditionally render an attribute, such as
class
/className
?- Pass
null
(common practice) orundefined
(less common) as the value. E.g.className={onSale ? 'sale' : null}
- Pass
- How do I force a component to be re-rendered / refs to be reset?
- This is actually another use for
key
s
- This is actually another use for
Miscellaneous Troubleshooting
- Error: React does not recognize the
___
prop on a DOM Element...- The React docs have a thorough page on possible causes and remedies
Tips
- You can assign JSX to variables! Makes it very easy to reuse!
- Make use of object destructuring assignments to avoid repetitive props syntax
const {hidden, dataArr, isAdmin, isAuthed} = this.props
- -> Or, destructure right in function argument,
const myComponent = ({hidden, dataArr, isAdmin, isAuthed}) => {}
- You can use class members and/or regular vars to hold changing values, as opposed to putting in state/props, if you want to avoid unnecessary renders when those values are not part of UI
Ways Around Prop-Drilling Hell
- Move state down, instead of up (making controlled components now uncontrolled from parent)
- Inline components, instead of breaking out as child components
- Use slots to pass whole components through tree (e.g. via
children
), instead of passing tons of props and reconstructing in child - Use context
- Lump together related props into objects
- Downside: this makes it harder to optimize against unnecessary re-renders
Tips: https://blog.logrocket.com/mitigating-prop-drilling-with-react-and-typescript/
Asset management and usage
You have two main options when it comes to asset management:
- Public Folder: Place in
/public
- Pull into HTML with
%PUBLIC_URL%
- Pull into JS with
process.env.PUBLIC_URL
- Pull into HTML with
- Bundling: Place asset files alongside JS, in
/src
- Pull into JS by using
import
at top of file with relatively path - Pull into css (e.g. for background image) with relative path
- Pull into JS by using
The second option is always preferred, since it uses webpack to bundle assets and will help keep the size of your app down by only bundling assets that are actually used (as well as some other tricks).
Importing assets in JS or CSS
Examples:
JS:
import background from './assets/background.jpg';
// ...
render() {
return (
<img src={background} />
)
}
CSS:
#main {
background-image: url(./assets/background.jpg);
}
Getting around method binding of this
A common issue with React is preserving the value of this
, especially when it comes to event handlers, like onClick
.
If the callback you are assigning uses this
within its body, you will have issues if the value of this
changes (most often in a Cannot read property '___' of undefined
error).
Here are some ways to ensure the value of this
stays the way you want it to:
Explicit, with .bind()
on the attribute
The older method was commonly to use .bind()
to explicitly bind the value of this at the point of attaching the handler. That might look something like this:
<CustomComponent onClick={this.handleEvent.bind(this)}>
ES6 Class binding
If you are using the new ES6 class syntax, you have all the other options, plus a few more. Let's say this is our base code:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {counter: 0};
}
handleClick(evt) {
const updatedCount = this.state.counter + 1;
this.setState({
counter: updatedCount
});
console.log(this.state.counter);
}
render() {
return (
<button onClick={this.handleClick}> Counter + </button>
);
}
}
One option is to use .bind()
, in the constructor:
constructor(props) {
super(props);
this.state = {counter: 0};
// ADDED:
this.handleClick = this.handleClick.bind(this);
}
Another option is to turn the handleClick
method, into a member, that is the value of an arrow function, thus automatically binding this
to the class:
// Rest of class definition
// Or: public handleClick = (evt) => {}
handleClick = (evt) => {
const updatedCount = this.state.counter + 1;
this.setState({
counter: updatedCount
});
console.log(this.state.counter);
}
// ...
Technically, this is actually an ES6 public field, which is, as of 2019, in an experimental stage, so while it is well supported with transpiled JS/TS, it has limited native browser support.
Inline arrow functions
One of the benefits (or drawbacks) of arrow functions is that they lexically bind this
- automatically. So, if you do:
<CustomComponent onClick={(evt) => {
this.handleEvent(evt);
}}>
It doesn't matter if CustomComponent
has a different this
scope, because the arrow function binds the callback where you defined it!
Use createReactClass
If you use createReactClass
, you generally don't have to worry about this issue, as createReactClass uses autobinding, which basically does .bind()
for you.
More reading
- http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html
- https://www.sitepoint.com/bind-javascripts-this-keyword-react/
Computed properties
Unlike Vue, which has a specific syntax for computed properties, React doesn't really care how you try to formulate values based on computed state properties.
ES6 Class - Getter
With the new ES6 class based approach, a very clean solution is to use getters, with something like:
class MyComponent extends React.Component {
// ... class stuff
get isAdult() {
return typeof(this.state.age) === 'number' && this.state.age > 18;
}
}
Within the render function itself
There is nothing preventing you from computing a value within the render
function itself. For example:
class MyComponent extends React.Component {
// ... class stuff
render() {
const isAdult = typeof(this.state.age) === 'number' && this.state.age > 18;
return (
<div>
Is adult = {isAdult.toString()}
</div>
);
}
}
As a function
Another vanilla solution here is to simply use a function to return the computed value, rather than anything else. How you declare this is up to you:
- Explicitly, with
function myFunction(){}
- As a ES6 class member
public isAdult() {}
- With an arrow function to bind
this
const isAdult = () => {};
- Etc.
To use within your render
method, simply make sure you actually execute it by following it with the parenthesis and arguments if applicable.
State management system
If you are using a dedicated state management system, there is a chance that what you are using might already have something in place to not only provide explicit computed properties, but also optimize them, to prevent redundant expensive re-calculations.
For example, if you use MobX, make sure to check out this page on computed values.
Further reading:
Handlebars / Mustache type logic (loops, conditional, etc)
Conditional rendering
Tons of options - see "7 ways to implement conditional rendering".
Some tricky ones to remember, but can be very helpful are:
- AND (
&&
) operator to show or hide- You can use
{shouldShowBool && <div>I'll only show up if shouldShowBool is true!</div>}
- This works because in JS,
A && B
returns B if A is true, without coercing B to a boolean - BEWARE of using numbers with
&&
; JS will coerce0
to false, but React will still render it. So use!!
orBoolean(myNumber)
for explicit conversion first.{0 && <p>test</p>}
renders0
{!!0 && <p>test</p>}
renders nothing
- You can use
- Ternary Operator (
_?_:_
) to swap between things- You can use
{isLeaving ? <p>Goodbye!</p> : <p>Hello!</p>}
- You can use
Loop / array render
The main way to render an array of something in React is to simply pass an array of JSX, within a JavaScript Expression block (e.g. {myJsxArr}
). However, there are many ways to write this code:
You could:
- Create an array of JSX template code assigned to a variable, and use it within
render()
, with something like:items.push(<div key={UNIQUE_KEY}>{CONTENT}</div>);
- Iterate over the items directly in
render()
, and return the template there:const render = function() { const items = ['bread', 'milk', 'cookies']; return ( <ul> {items.map((value, index) => { return <li key={index}>{value}</li> })} </ul> ); }
- Use an IIFE to construct and return the array, all inline within the JSX:
<div> <h1>One through Ten:</h1> {(function () { const jsxArr = []; for (let x = 1; x <= 10; x++) { jsxArr.push(<p key={x}>Number {x}</p>); } return jsxArr; })()} </div>
- This is kind of an anti-pattern, so I would avoid and instead construct the array further up in the component before starting to return JSX / HTML.
Composing Components
Composing Components - Syntax
When composing components, don't forget that within the same file you can even create and render components with the familiar angle-bracket JSX syntax. For example, these both work, but the second version is more readable as part of a standard React codebase:
const Hello = () => (
<p>Hello!</p>
)
const MyComponent = ({name}) => {
const greeting = `Welcome ${name}`;
return (
<div>
{Hello()}
<p>{greeting}</p>
</div>
)
}
// VERSUS
const MyComponent = ({name}) => {
const greeting = `Welcome ${name}`;
return (
<div>
<Hello />
<p>{greeting}</p>
</div>
)
}
React Hooks
React - TypeScript and TSX
👉I also have a general cheat sheet on TypeScript you might find helpful!
React - TypeScript and TSX - Resources
- My General TS Cheat Sheet
- typescript-cheatsheets/react
- TypeScript Handbook - JSX
- @ddprrt: TypeScript and React: Components
React - Common TS Issues
- Error using component:
'__' cannot be used as a JSX component. Its return type '___' is not a valid JSX element.
+ts(2786)
- Read the full error message to be sure, but this could be because you indeed have something unsafe or incorrectly typed
- In some cases, this can be caused by type definition files getting "out of sync". Especially if the specific error is that the return type might be
null
, but you know it shouldn't be- Try completely deleting
node_modules
, all lock files, and then re-installing
- Try completely deleting
- Does adding
@types/react
as a devDependency fix it? - Are you using
Preact
? You might want to take a look at scurker/preact-and-typescript guide, or see if this specific issue (around preact/compat) applies.
Cannot find name '___'
- Did you accidentally name your file with
.ts
instead of.tsx
? - Make sure you have configured the
jsx
andjsxFactory
incompilerOptions
in the TSConfig
- Did you accidentally name your file with
- Type errors trying to set a CSS Custom Property / CSS variable directly inside inline styling (
type '{ '--my-css-var': string }' is not assignable to type ...
)- You could augment the
React.CSSProperties
definition, but it is usually faster and easier to just use inline casting:style={{--my-css-var: "purple"} as React.CSSProperties}
- Different approaches covered in responses to this StackOverflow question
- You could augment the
React - Common TS Syntax Stuff
- Declaring prop types
// All of the below are acceptable
// We can define the shape of props separately
interface BirthdayProps {
age: number;
name: string;
}
export const BirthdayGreeting = (props: BirthdayProps) => (
<span>{`Congrats ${props.name} on turning ${props.age}`}</span>
);
// ...or, we can inline it
export const BirthdayGreeting = (props: { age: number; name: string }) => (
<span>{`Congrats ${props.name} on turning ${props.age}`}</span>
);
// And we can also use destructuring for easier nested prop access:
export const BirthdayGreeting = ({ age, name }: { age: number; name: string }) => (
<span>{`Congrats ${name} on turning ${age}`}</span>
);
- Reusing Prop Types / Extracting Prop Types
- You can use
ComponentProps<typeof MyComponent>
(orComponentPropsWithoutRef
)- For standard elements, you can use with string constant
ComponentProps<'div'>
- For standard elements, you can use with string constant
- Optimally, if using a library that separately exports Prop types, import & use that directly (e.g.
import {BoxProps} from '@chakra-ui/core'
) - Another alternative is
JSX.IntrinsicElements["div"]
, but this is not recommended. - See this S/O answer
- TS React Cheatsheet: "Wrapping/Mirroring a HTML element
- You can use
- Taking generic props / extending another component props
- It kind of depends on how specific you want to be, and what exact ecosystem you are using (
react
,preact
, etc.) - It is usually best to combine your custom props with either:
- A props interface exported from your framework (e.g.
BoxProps
from Chakra UI)- Or...
React.ComponentProps<T>
utility- See reusing prop types section above for details
- A props interface exported from your framework (e.g.
- Example: Custom component that allows parent to pass all standard
<div>
props 👇interface Props { name: string; } const MyComponent = (props: Props & ComponentProps<'div'>) => { return ( <div {...props} className={['myComponent', props.className].join(' ')}> <span>Hello {props.name}</span> </div> ) };
- It kind of depends on how specific you want to be, and what exact ecosystem you are using (
- Accepting components through props / passing children
- The type signature of children is kind of tricky in React. In general, practically anything can be a child, so the type is pretty wide.
- In standard React:
- the type for props that accept children looks like this:
P & { children?: ReactNode };
(src) - And
ReactNode
can practically be anything:type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
(src)- This gets expanded even further...
- You can use
React.propsWithChildren<P>
as shorthand forP & { children?: ReactNode }
- See React TypeScript Cheatsheet - Useful React Prop Types for more samples
- the type for props that accept children looks like this:
- In
preact
:
- Typing DOM events and change handlers
- There are multiple ways to do this
- Be careful about where types are coming from! E.g., sometimes you want
React.MouseEvent
instead ofMouseEvent
- If you are trying to type the
evt
for an event handler, you can...- Use a type that accepts a generic slot for the event-generating element:
evt: MouseEvent<{ElementType}>
evt: TargetedEvent<{ElementType}, Event>
evt: TargetedEvent<{ElementType}, {EventSubType}>
- Example:
TargetedEvent<HTMLButtonElement, MouseEvent>
- Some versions of React:
evt: ChangeEvent<{ElementType}>
- Example:
evt: ChangeEvent<HTMLSelectElement>
- Merge a generic event type with an explicit type for
evt.target
:evt: MouseEvent<HTMLButtonElement> & {target: HTMLButtonElement}
evt: h.JSX.TargetedEvent<HTMLSelectElement, Event> & {target: HTMLSelectElement}
evt: InputEvent & {target: HTMLSelectElement}
- Use the direct type, if you have it:
onClick={(evt: MouseEvent) => {}}
- Use a type that accepts a generic slot for the event-generating element:
- If you are trying to type the element of
evt.target
, you can just type assert for that section:evt.target as HTMLInputElement
- Some of this also depends on React vs Preact, framework libs, etc.
- Declaring a type for a generic JSX / React component:
- Most of the time, you can simply use
ComponentType<P>
, whereP
is a generic slot for props. - Alternative, you can use one the two types that the above is a union for:
ComponentClass<P>
FunctionComponent<P>
or its alias,FC<P>
- Most of the time, you can simply use
React - Enforcing Strong Types From Generic Props
Inferring a type based on a generic is somewhat common TS task, and not limited to the scope of React. However, there is a use-case for strong prop type inference that comes up often enough it's worth its own example; form option callbacks.
A common need in React is to have a form handler, which might be a custom component as a wrapper around form elements, hooks, classes, etc. For a handler around a picker element, such as <select>
, we might want extra strong types, since we know the options ahead of time, and what all the possible values are.
In order to have our wrapper accept a list of possible values and strongly infer the type of the selected option and pass it to a callback, we need to use two main tricks:
- Type literal assertions - these take the form of
- Utility type:
ReadOnly<T>
- const assertions:
const me = {name: 'joshua'} as const
- Available since
v3.4
- Available since
- Utility type:
- Generics and generic indexes
Putting these together to produce a solution: CodeSandbox Link
Full Code:
Select.tsx
import { ChangeEvent } from "react";
interface SelectPair {
value: string;
display: string;
}
export const SelectWithTypedCallback = <T extends readonly SelectPair[]>({
options,
onChange = () => {}
}: {
options: T;
onChange?: (value: T[number]["value"]) => void;
}) => {
const changeHandler = (evt: ChangeEvent<HTMLSelectElement>) => {
onChange(evt.target.value);
};
return (
<select onChange={changeHandler}>
{options.map((o) => (
<option value={o.value} key={o.value}>
{o.display}
</option>
))}
</select>
);
};
export default SelectWithTypedCallback;
App.tsx
:
import { SelectWithTypedCallback } from "./Select";
// Notice the use of `as const` to mark this as ReadOnly / frozen
const options = [
{
value: "dog",
display: "Doggie! 🐶"
},
{
value: "cat",
display: "Kittie! 🐱"
}
] as const;
export default function App() {
return (
<div className="App">
<SelectWithTypedCallback
options={options}
onChange={(choice) => {
// Through the power of generics,
// `choice` is magically inferred as type of `cat | dog` !!!
console.log(choice);
}}
/>
</div>
);
}
Material-UI Notes
Material-UI: Media Queries
In general, the docs on breakpoints are a good place to start. Here are a few options:
- You can use them directly in
makeStyles
:const useStyles = makeStyles(theme => ({ myStyle: { width: '100px', '@media (min-width: 1000px)': { width: '400px', }, // Or, with theme settings // https://material-ui.com/customization/breakpoints/#css-media-queries [theme.breakpoints.up('sm')]: { width: '200px', }, } }));
- You can use the provided
useMediaQuery
hooks