React Hooks Cheatsheet

React Hooks are functions which enhance your React components with different capabilities. They were designed as a major update to the React API, allowing to stay away from class components and their limitations & pitfalls.

This means that some component features should be done differently, and sometimes it's not entirely intuitive to go from the component approach to the React Hooks approach. So we've created this document to help you use hooks. With the examples below, there should be no need for creating class components anymore !

See this video for more information about React Hooks: https://reactjs.org/docs/hooks-intro.html

state

Simple example with a local state value held withing the component itself.

Before

class Foo extends React.Component {
state = {
bar: true,
};
render() {
return (
<View>
<Text>Bar is {this.state.bar}</Text>
<Button
label="set Bar to false"
onPress={() => this.setState({ bar: false })}
/>
<Button
label="set Bar to true"
onPress={() => this.setState({ bar: true })}
/>
</View>
);
}
}

with Hooks

function Foo() {
const [bar, setBar] = React.useState(true);
return (
<View>
<Text>Bar is {bar}</Text>
<Button label="set Bar to false" onPress={() => setBar(false)} />
<Button label="set Bar to true" onPress={() => setBar(true)} />
</View>
);
}

complex state / reducer

Sometimes you have a slightly more complex state, which can hold multiple values, or require to update the state based on previous values. This when you want to use the reducer hook.

Before

class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: this.props.initialValue || 0,
};
}
reset = () => this.setState({ counter: this.props.initialValue || 0 });
inc = (val) =>
this.setState((prevState) => ({ counter: prevState.counter + val }));
dec = (val) =>
this.setState((prevState) => ({ counter: prevState.coutner - val }));
render() {
return (
<View>
<Text>Counter is {this.state.counter}</Text>
<Button label="Reset" onPress={() => this.reset()} />
<Button label="Increment" onPress={() => this.inc(1)} />
<Button label="Decrement" onPress={() => this.dec(1)} />
</View>
);
}
}

with Hooks

const initializeState(initialValue) {
return { counter: initialValue };
}
const ACTIONS = {
RESET: val => ({ type: "RESET", payload: val }),
INC: val => ({ type: "INC", payload: val }),
DEC: val => ({ type: "DEC", payload: val })
};
function reducer(state, { type, payload} ) {
switch(type) {
case "RESET":
return { counter: payload };
case "INC":
return { counter: state.counter + payload }
case "DEC":
return { counter: state.counter - payload }
default:
return state;
}
}
function Foo({ initialValue = 0 }) {
const [state, dispatch] = React.useReducer(reducer, initialValue, initializeState);
return (
<View>
<Text>Counter is {state.counter}</Text>
<Button label="Reset" onPress={() => dispatch(ACTIONS.RESET(0)) } />
<Button label="Increment" onPress={() => dispatch(ACTIONS.INC(1)) } />
<Button label="Decrement" onPress={() => dispatch(ACTIONS.DEC(1)) } />
</View>
)
}

Context

Contexts allow to share values across different components, regardless of how many levels there are between the provider & the consumer of the context. The creation of the context and the placement of the context provider is done the same way. But now, contexts can be consumed without requiring to use a decorator or a render prop function

Before

const Context = React.Context(true);
function ContainsFoo() {
return (
<Context.Provider value={true}>
<Foo />
</Context.Provider>
);
}
function Foo() {
return (
<Context.Consumer>
{(bar) => (
<View>
<Text>Bar is {bar}</Text>
</View>
)}
</Context.Consumer>
);
}

with Hooks

const Context = React.Context(true);
function ContainsFoo() {
return (
<Context.Provider value={true}>
<Foo />
</Context.Provider>
);
}
function Foo() {
const bar = React.useContext(Context);
return (
<View>
<Text>Bar is {bar}</Text>
</View>
);
}

componentDidMount

When you want a certain call to be done when your component mounts. Don't forget the arguments to pass to your function, as a second argument to React.useEffect. You can even inject values from props or state. If you don't want to pass any argument to your onMount function, you should still call React.useEffect with an empty array as second argument. otherwise this effect will be call on each render (see below)

function onMount(arg1, arg2) {
// ...
}
React.useEffect(onMount, [arg1, arg2]);

Before

class Foo extends React.Component {
componentDidMount() {
doSomething();
}
render() {
return <View />;
}
}

with Hooks

function Foo() {
React.useEffect(() => {
doSomething();
}, []);
return <View />;
}

side effect in render / componentDidUpdate

React components should be a pure function of props, but sometimes you have side effects. This one is really bad in the class component syntax, and will trigger warnings in the console. But if you really need to trigger a side effect every time a component is rendered, this can be achieved in a much more reliable and safer way with React Hooks. for this, just leave the second argument of React.useEffect empty.

Before

class Foo extends React.Component {
render() {
doSomething(); // you shouldn't really
return <View />;
}
}

with Hooks

function Foo() {
React.useEffect(() => {
doSomething();
}); // side effect will run on each render cycle, and not only on mount
// it is safe !
return <View />;
}

componentWillUnmount

If you want to do something when your component unmouts, this is the way. Pay close attention to the syntax below: the block which runs when the component unmounts is the function returned from the argument passed to React.useEffect

function onUnmount() {
// ...
}
function onMount() {
//...
return onUnmount;
}
React.useEffect(onMount);

Before

class Foo extends React.Component {
componentWillUnmount() {
doSomething();
}
render() {
return <View />;
}
}

with Hooks

function Foo() {
React.useEffect(() => {
return () => {
doSomething();
};
}, []);
return <View />;
}

componentDidMount & componentWillUnmount

You can have onMount and onUnmount together by mixing the two examples above. Since onUnmount is the return value of unMount it makes it very easy to share a variable between mount & unmount phases, as they belong to the same closure.

Before

class Foo extends React.Component {
componentDidMount() {
doSomethingOnMount();
}
componentWillUnmount() {
doSomethingOnUnmount();
}
render() {
return <View />;
}
}

with Hooks

function Foo() {
React.useEffect(() => {
doSomethingOnMount();
return () => {
doSomethingOnUnmount();
};
}, []);
return <View />;
}

componentDidUpdate, watching specific props / state

When you want to run something when the component's props or state properties change. Caution, just like componentDidUpdate, if you're not careful of what you do, you can end up with an infinite render loop.

Before

class Foo extends React.Component {
componentDidUpdate() {
doSomething;
}
render() {
return <View />;
}
}

with Hooks

function Foo(props) {
const [bar, setBar] = React.useState(true);
React.useEffect(() => {
doSomething();
}, [props, bar]); // you can pass anything you want, the effect function will be called whenever this input changes.
return <View />;
}

shouldComponentUpdate

This can be used to tell React whether a component should re-render or not. The paradigm here for hooks is different - we're using the memoization feature from React.memo

Before

class Foo extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// return true or false
}
render() {
return <View />;
}
}

with Hooks

function shouldUpdate(previousProps, nextProps) {
// return true or false
}
const Foo = React.memo((props) => {
return <View />;
}, shouldUpdate);

PureComponent

PureComponent is a type of class component which will only update when props change, performing a shallow comparision of props (so non-primitive props will always trigger a re-render as they are compared references and not values).

This can be achieved with React.memo.

Before

class Foo extends React.PureComponent {
render() {
return <View />;
}
}

with Hooks

const Foo = React.memo((props) => {
return <View />;
});

Component Ref

Sometimes you need to access a reference to a children component. That's possible via hooks too.

Before

class Foo extends React.Component {
assignRef = (ref) => (this.ref = ref);
componentDidUpdate() {
if (this.ref) {
this.ref.callSomethingOnRef();
}
}
render() {
return <ChildComponent ref={this.assignRef} />;
}
}

with Hooks

function Foo(props) {
const ref = React.useRef(null);
React.useEffect(() => {
if (ref) {
ref.callSomethingOnRef();
}
}, [props]);
return <ChildComponent ref={ref} />;
}

That's it ! the Hooks API is more powerful and allows for even more cool stuff. If you want to dig the topic, you can go here : https://dev.to/dan_abramov/making-sense-of-react-hooks-2eib https://reactjs.org/docs/hooks-intro.html