Hooks in React Native
By: Marius Reimer
Tags: JavaScript, React, Software Development, React Native, Software Engineering

These are the most important things you should know about a React Component and its lifecycle:

Props

Props are input of a component, so it is something you put into a component when you create it. Per definition props cannot change, but you can add a function to the props that do that for you (could be confusing).

State

State is something that can dynamically change (like a text input) and is always bound to something (a component for example). You can change the state by using the setState() function, which only notifies the component about a state change. Take a look at the following example and common pitfall with React and setState() :

// not so good

console.log(this.state.test); // 5

this.setState({ test: 12 });

console.log(this.state.test); // might be 5 or 12 // good

this.setState({ test: 42 }, () => { console.log(this.state.test); // 42 });

Constructor

The constructor is not always necessary to have. However, there are some uses cases: initializing state and binding methods to this . What you definitely should not do there is invoking long-running methods, since this may slow down your initial rendering (see the diagram above). So a common component and constructor could look like the following:

class MyComponents extends React.Component {

constructor(props) {

super(props); this.state = {

test: 42

}; this.renderSomeText = this.renderSomeText.bind(this);

} // you could also do this, so no constructor needed

state = {

test: 42,

} renderSomeText() {

return <Text>this.state.test</Text>

}

}

If you don’t bind method in the constructor and only initialize the state, you don’t even need a constructor (save code). See my article about React Performance here, if you don’t know why you should bind certain methods. This also has some valueable code examples.

Component did mount and will unmount

The componentDidMount lifecycle method is invoked only once after the component was rendered for the first time. This could be the place where you do requests or register event listeners, for example.

Apart from that, the componentWillUnmount lifecycle method is invoked before the component is getting “destroyed”. This should be the place where you cancel eventually running requests (so they don’t try to change the state of an unmounted component or something), as well as unregister any event listener you use. Lastly will prevent you from having memory leaks in your app (memory that is not being used is not released).

A problem probably many (and also I) ran into was exactly what I described in the last paragraph. If you use the Window setTimeout function to execute some code in a delayed manner, you should take care of using clearTimeout to cancel this timer if the component unmounts.

Other lifecycle methods

The componentWillReceiveProps(nextProps) or from React Version 16.3 getDerivedStateFromProps(props, state) lifecycle method is being used to change the state of a component when its props changed. Since this is a more complex topic and you probably use (and should use) it rarely, you can read about it here.

Difference between Component and PureComponent: You might have heard about React’s PureComponent already. To understand its difference, you need to know that shouldComponentUpdate(nextProps, nextState) is used/called to determine whether the change in props and state should trigger a re-rendering of the component. The normal React.Component always re-renders, on any change (so it always returns true). The React.PureComponent does a shallow comparison on props and state, so it only re-renders if any of them have changed. Keep in mind that if you change deeply nested objects (you mutate them), shallow compare might not detect it.

If you ask yourself where hooks fit into this lifecycle, the answer is pretty easy. One of the most important hooks is useEffect. You pass a function to useEffect , which will run after the render call. So in essence, it is equal to componentDidUpdate. If you return a function from the useEffect’s passed function, you can handle the componentWillUnmount code. Since useEffect runs after every render (which might not always make sense), you can limit it to being closer to componentDidMount and componentWillUnmount by passing [] as a second argument. This tells React that this useEffect should only be called when a certain state has changed (in this case [], which means only once).

The most interesting hook is useState . It’s usage is pretty simple: You pass an initial state and get a pair of values (array) in return, where the first element is the current state and the second a function that updates it (like setState() ). If you want to read more about hooks, check out the React documentation.

Lastly, I want to present a simple example of a React Native component with React Hooks. It contains a View with a Text and Button component. By clicking the button, you increase the counter by 1. If the counter reaches value 42 or greater, it stays at 42. You can argue if it makes sense or not. Especially since the value will shortly be increased to 43, then render once, then the useEffect will set it back to 42.

import React, { useState, useEffect } from 'react';

import { View, Text, Button } from 'react-native'; export const Example = () => {

const [foo, setFoo] = useState(30); useEffect(() => {

if (foo >= 42) {

setFoo(42);

}

}, [foo]) return (

<View>

<Text>Foo is {foo}.</Text>

<Button onPress={() => setFoo(foo + 1)} title='Increase Foo!' />

</View>

)

}

React Hooks are a great way to write even cleaner React components. Its natural ability to create reusable code (you can combine your hooks) makes it even greater. The fact that cleaning side effects (subscriptions, requests) happen for every render by default helps avoid bugs (you may forget unsubscribe), as stated here.