Home >Web Front-end >JS Tutorial >State Management in React Native
Core points
setState()
method, the Context API, and recently introduced Hooks. State management is one of the most difficult concepts to master in learning React Native, as there are many ways to implement it. There are countless state management libraries on the npm registry—such as Redux—and there are countless libraries built on the other state management libraries to simplify the original library itself—such as Redux Easy. A new state management library is introduced in React every week, but since the introduction of React, the basic concept of maintaining application state remains the same.
The most common way to set state in React Native is to use React's setState()
method. We can also use the Context API to avoid props drilling and pass multi-layer state down without passing it to individual subcomponents in the tree.
Recently, Hooks appeared in React v16.8.0, a new mode that simplifies the use of state in React. React Native got it in v0.59.
In this tutorial, we will learn what state actually means, as well as the setState()
method, the Context API, and React Hooks. This is the basis for setting state in React Native. All libraries are built based on the above basic concepts. So once you understand these concepts, it will be easy to understand the library or create your own state management library.
Want to learn React Native from scratch? This article is excerpted from our advanced library. Join SitePoint Premium now for a complete React Native book collection covering basics, projects, tips, and tools, for just $9 per month.
What is status?
Anything that changes over time is called a state. If we have a counter application, the state is the counter itself. If we have a to-do app, the to-do list changes over time, so this list will be status. Even the input element is a state in a sense, as it changes over time as the user types it in.
setState Introduction
Now that we know what state is, let's understand how React stores it.
Consider a simple counter application:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0, }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState({ counter: counter + 1 })} /> <button title="Decrement" onpress="{()"> this.setState({ counter: counter - 1 })} /> </> ); } }</button></button></code>
In this application, we store the state within an object in the constructor and assign it to this.state
.
Remember that state can only be one object. You cannot store numbers directly. This is why we create a counter
variable inside the object.
In the render
method, we deconstruct the this.state
attribute from counter
and render it within <h1></h1>
. Note that at present it will only display static values (0).
You can also write states outside the constructor as follows:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0 }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState({ counter: counter + 1 })} /> <button title="Decrement" onpress="{()"> this.setState({ counter: counter - 1 })} /> </> ); } }</button></button></code>
Now suppose we want the "" and "-" buttons to work. We have to write some code inside their respective onPress
handlers:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0 }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter + 1 }))} /> <button title="Decrement" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter - 1 }))} /> </> ); } }</button></button></code>
Now, when we click the " " and "-" buttons, React re-renders the component. This is because the setState()
method is used.
setState()
method re-renders part of the changed tree. In this case, it re-renders <h1></h1>
.
So if we click " , it will increase the counter by 1. If we click "-", it will reduce the counter by 1.
Remember that you cannot change the status directly by changing this.state
; executing this.state = counter 1
will not work.
In addition, the state change is an asynchronous operation, which means that if you read this.setState
immediately after calling this.state
, it will not reflect the latest changes.
Here, we use the "function as callback" syntax to use setState()
as shown below:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0 }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter + 1 }))} /> <button title="Decrement" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter - 1 }))} /> </> ); } }</button></button></code>
"Function as callback" syntax provides the latest state - in this case prevState
- as a parameter to the setState()
method.
In this way, we can get the latest changes to the state.
What are Hooks?
Hooks is a new feature in React v16.8. Earlier, you could only use state by creating class components. You cannot use state in the function component itself.
With the addition of Hooks, you can use state in the function component itself.
Let's convert the above Counter
class component to Counter
function component and use React Hooks:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; const Counter = () => { const [counter, setCounter] = React.useState(0); return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> setCounter(counter + 1)} /> <button title="Decrement" onpress="{()"> setCounter(counter - 1)} /> </> ); };</button></button></code>
Note that we reduced the number of lines of code for a class component from 18 to only 12 lines. In addition, the code is easier to read.
Let's review the above code. First, we use React's built-in useState
method. useState
can be of any type—such as a number, string, array, boolean, object, or any type of data—unlike setState()
, setState()
can only have one object.
In our counter example, it takes a number and returns an array containing two values.
The first value in theThe first value in the array is the current state value. Therefore, counter
is currently 0.
The second value in the array is a function that allows you to update the state value.
In our onPress
, we can use setCounter
to update counter
directly.
Therefore, our incremental function becomes setCounter(counter 1)
and our decremental function becomes setCounter(counter - 1)
.
React has many built-in Hooks such as useState
, useEffect
, useContext
, useReducer
, useCallback
, useMemo
, useRef
, useImperativeHandle
, useLayoutEffect
, useDebugValue
,
,
and——You can find more information in the React Hooks documentation.
useState
Two rules are required to be followed when building or using Hooks: useEffect
Call Hooks from React functions only. Do not call Hooks from regular JavaScript functions. Instead, you can call Hooks from the React function component or from custom Hooks.
By following this rule, you can ensure that all stateful logic in the component is clearly visible from its source code. Hooks are very easy to understand and are useful when adding state to function components.
Context APIContext provides a way to pass data in a component tree without manually passing props at each layer. theme
App
In a typical React Native application, data is passed from top to bottom via props. If there are multiple component levels in the React application and the last child component in the component tree wants to retrieve data from the topmost parent component, you must pass props down individually. Pic
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0, }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState({ counter: counter + 1 })} /> <button title="Decrement" onpress="{()"> this.setState({ counter: counter - 1 })} /> </> ); } }</button></button></code>from the
component to the theme
component. Normally, if we don't use Context, we will pass it through each intermediate level, like so:
The value of
is passed from App -> Home -> Profile -> Pic. The above problem is called props drilling.
This is a simple example, but consider a real application with dozens of different levels.
It becomes difficult to pass data through each child component just to use it in the last child component. So we have Context. Context allows us to pass data directly from App -> Pic.The following is how to use the Context API:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0, }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState({ counter: counter + 1 })} /> <button title="Decrement" onpress="{()"> this.setState({ counter: counter - 1 })} /> </> ); } }</button></button></code>
First, we use the React.createContext
API to create ThemeContext
. We set light
to the default value.
Then, we use the theme
to wrap the root element of the ThemeContext.Provider
component with the App
when we provide
ThemeContext.Consumer
Finally, we use theme
as the rendering prop to get the dark
value is
ThemeContext.Consumer
Rendering prop mode is good, but if we have multiple contexts, it can cause callback hell. To avoid callback hell, we can use Hooks instead of
Profile
The only thing we need to change is the
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0 }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState({ counter: counter + 1 })} /> <button title="Decrement" onpress="{()"> this.setState({ counter: counter - 1 })} /> </> ); } }</button></button></code>
In this way, we don't have to worry about callback hell.
Shared state across components
So far, we have only managed state in the component itself. Now we will understand how to manage state between components.
Suppose we are creating a simple to-do list application as shown below:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0 }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter + 1 }))} /> <button title="Decrement" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter - 1 }))} /> </> ); } }</button></button></code>
AddTodo
Now, if we want to add a to-do from the TodoList
component, how will it be displayed in the todos
prop of the
If two sibling components want to share the state, they must be promoted to the parent component. The complete example should look like this:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; class Counter extends React.Component { state = { counter: 0 }; render() { const { counter } = this.state; return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter + 1 }))} /> <button title="Decrement" onpress="{()"> this.setState(prevState => ({ counter: prevState.counter - 1 }))} /> </> ); } }</button></button></code>
App
Here, we save the state in the useState
component. We use React Hook todos
to store
addTodo
Then we pass the AddTodo
method to the todos
component and pass the TodoList
array to the
The AddTodo
addTodo
component receives the
TextInput
We also have a useState
element, which also uses React Hook TextInput
to track the change value of
App
After pressing the button, we will call the addTodo
method passed from the parent todos
. This ensures that the to-do items are added to the TextInput
list. We will clear the
TodoList
todos
The component receives
You can also try to delete a to-do item to practice improving yourself. Here is the solution:
<code class="language-javascript">import React from 'react'; import { Text, Button } from 'react-native'; const Counter = () => { const [counter, setCounter] = React.useState(0); return ( <> <text>{counter}</text> <button title="Increment" onpress="{()"> setCounter(counter + 1)} /> <button title="Decrement" onpress="{()"> setCounter(counter - 1)} /> </> ); };</button></button></code>
This is the most common practice in React. The improvement is not as simple as it seems. This is a simple example, but in a real application we don't know which state needs to be promoted to its parent for use in sibling components. Therefore, first, the state is kept in the component itself, and only when a situation occurs when it is necessary to share the state between components, the state is promoted to the parent.
In this way, you do not make the parent component a large state object.
Conclusion
All in all, we understand what state is and how to set state values using the setState()
API provided by React. We also learn about React Hooks, which makes it easy to add state to function components without converting them into class components.
We learned the new Context API and its Hooks version useContext
, which helped us avoid rendering prop callback hell.
Finally, we learned how to improve state to share state among sibling components.
React becomes very simple once you understand these core concepts. Remember to keep the state local to the component as much as possible. Use the Context API only if props drilling becomes a problem. Promote state only if needed.
Lastly, once your application becomes complex and difficult to debug state changes, check out state management libraries like Redux and MobX.
FAQs about React Native State Management
What is state management in React Native? State management in React Native refers to the management and processing of state (data and UI logic) in a React Native application. It involves efficiently updating and synchronizing states in different components of an application.
Why is state management important in React Native development? State management is critical to React Native because it maintains and updates the application's dynamic data and user interface. It ensures that changes in part of the application are accurately reflected in other parts, providing a seamless and responsive user experience.
What are the different ways to manage state in React Native? React Native provides a variety of state management methods, including local component state, React Hooks (useState
), Redux, MobX, and context APIs. The choice depends on the complexity and specific requirements of the application.
When should I use local component state with global state management solutions like Redux or MobX? For simple, local state management within components, use local component state. For complex applications that share state among multiple components, consider using global state management solutions such as Redux or MobX to maintain centralized and easily accessible state.
How does the Context API facilitate state management in React Native? The Context API is a feature in React that allows components to share state without explicitly passing props through component trees. It is useful for managing global state without the need for additional libraries like Redux.
This revised output maintains the original image locations and formats, rephrases the text for originality while preserving the core meaning, and addresses the prompt's requirements.
The above is the detailed content of State Management in React Native. For more information, please follow other related articles on the PHP Chinese website!