При разработке надежных веб- или мобильных приложений (apps) внутренняя обработка состояния, как это принято в компонентах React Native, не всегда идеальна. При неправильном подходе это может быстро стать очень грязным. В подобных ситуациях обычно рекомендуется использовать библиотечные инструменты, такие как Redux.
В этой статье мы рассмотрим различные шаги по управлению потоком состояний в простом приложении React Native с помощью набора инструментов Redux.
- Что такое Redux & Redux Toolkit?
- Глоссарий Redux
- Действие
- Редуктор
- Диспетчер
- Slice
- Магазин
- Использование набора инструментов Redux с React Native
- Предварительные условия
- Настройка проекта
- Создание интерфейса приложения
- Настройка хранилища Redux
- Создание фрагментов набора инструментов Redux
- useSelector и useDispatch
- Когда использовать Redux?
- Заключение
Что такое Redux & Redux Toolkit?
Redux — это библиотека глобального управления состоянием на Javascript, предназначенная для работы в качестве центрального хранилища для управления состоянием приложения. Redux помогает нам создавать приложения, которые ведут себя последовательно во всех средах, предоставляя систему для отслеживания всех изменений, внесенных в состояние.
Redux Toolkit — это официальный набор инструментов Redux для разработки эффективных приложений React-Redux. Он был разработан для упрощения написания общей логики Redux и решения обычных трудностей, связанных с использованием основной библиотеки Redux.
Например:
- Настройка хранилища Redux
- Создание фрагментов состояния редуктора
- Написание неизменяемого кода обновления состояния
При использовании Redux Toolkit вместо основного Redux способ реализации этих функций меняется.
Глоссарий Redux
Действие
Действие — это простой объект, который указывает на желание изменить состояние в хранилище Redux. Требуется, чтобы действия указывали полезную нагрузку и атрибут типа, который описывает, какого рода изменение должно быть сделано в состоянии. Для успешного выполнения действий требуются редукторы.
Редуктор
Редуктор — это чистая функция, которая принимает два аргумента: текущее состояние и действие, возвращающее результат нового состояния. Редукторы не изменяют исходное состояние напрямую; скорее, они создают копию состояния и изменяют ее.
Диспетчер
Диспетчерская функция» — это функция, которая принимает синхронный или асинхронный объект действия и отправляет его на выполнение редуктору.
Slice
Набор редукторов и действий, которые работают вместе для реализации одной функции приложения.
Магазин
Согласно официальной документации Redux, магазин — это объект, который хранит все дерево состояний приложения. В приложении Redux может быть только один магазин.
Использование набора инструментов Redux с React Native
Предварительные условия
- Базовые знания о React Native.
- Node.js LTS >= v14.x.x (рекомендуется v16.14.2).
- Установлен менеджер пакетов, например npm или yarn.
- Установлен Expo CLI.
- Знание концепций Redux.
ПРИМЕЧАНИЕ: Эта статья не является учебником по React Native и не будет посвящена концепциям React Native.
Настройка проекта
Приложение, которое мы будем создавать, представляет собой простой генератор случайных цветов, и для простоты мы будем использовать Expo CLI для создания и запуска нашего приложения.
Полный исходный код приложения вы можете найти на этом репозитории Github.
В терминале вашего компьютера выполните следующие команды:
expo init redux-toolkit-guide
cd redux-toolkit-guide
В качестве шаблона выберите ‘- Managed Workflow-blank’.
Теперь мы установим некоторые необходимые зависимости для нашего приложения, включая @react-navigation/native
, react-native-screens
и react-native-safe-area-context
.
Последние инструкции по установке см. в официальной документации по библиотеке React Navigation.
yarn add @react-navigation/native
expo install react-native-screens react-native-safe-area-context
Создание интерфейса приложения
Откройте приложение redux-toolkit-guide в редакторе кода (рекомендуется VS Code) и создайте структуру файлов.
- Создайте папки
src
иstore
в корне проекта. - Внутри
store
создайте файлcolorSlice.js
иstore.js
. - Создайте папку
screen
внутриsrc
. - Внутри папки
screens
создайте файлHomeScreen.js
и импортируйте приведенный ниже код.
import React from "react";
import { StatusBar } from "expo-status-bar";
import {
Text,
View,
StyleSheet,
TouchableOpacity,
FlatList,
} from "react-native";
const HomeScreen = () => {
return (
<View>
<StatusBar style="dark" />
<TouchableOpacity
onPress={() => //empty anonymous function}
style={styles.button}
>
<Text style={{ fontSize: 20 }}>Generate Random Color</Text>
</TouchableOpacity>
<FlatList
keyExtractor={(item) => item}
data={color}
style={{ marginTop: 15 }}
renderItem={({ item }) => {
return (
<View
style={{
backgroundColor: item,
height: 150,
width: 150,
alignSelf: "center",
margin: 10,
}}
/>
);
}}
/>
</View>
);
};
export default HomeScreen;
const styles = StyleSheet.create({
button: {
alignSelf: "center",
borderWidth: 1,
borderRadius: 10,
padding: 10,
marginTop: 20,
},
});
src/screens/HomeScreen.js
Переопределите App.js
следующим фрагментом кода:
import * as React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import HomeScreen from "./src/screens/HomeScreen/HomeScreen";
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default () => {
return <App />;
};
App.js
Запускаем expo start
в нашем терминале, чтобы запустить среду разработчика. Наше приложение должно выглядеть следующим образом:
Настройка хранилища Redux
Redux Toolkit сокращает длину логического кода Redux, который мы должны написать в нашем приложении. Он использует API configureStore
вместо API createStore
из основного Redux для создания магазина. configureStore
также автоматически устанавливает расширение Redux DevTools и некоторое промежуточное ПО.
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: { },
});
store/colorSlice.js
В хранилище хранится один корневой объект reducer для всех срезов состояния в приложении.
Теперь нам нужно обернуть наш компонент приложения с store
с помощью React-Redux Provider
. Это гарантирует, что наш Redux store
находится на самом верхнем уровне и доступен всему приложению React Native.
...
import { store } from "./store/store";
import { Provider } from "react-redux";
...
export default () => {
return (
<Provider store={store}>
<App />
</Provider>
);
};
App.js
Создание фрагментов набора инструментов Redux
Далее мы создадим фрагмент состояния для обработки всех действий и функций редуктора, связанных с генерацией случайного цвета в нашем приложении. Импортируя и вызывая createSlice
в коде ниже, мы определяем внутри него;
name
для идентификации среза.- значение
initialState
. Оно определяет, каким будет состояние при первом запуске приложения (как и при использовании хука ReactuseState
). - функция
reducer
, определяющая, как будет обновляться состояние. В блоке кода мы берем результат функции randomRgb и добавляем его к исходному массиву цветов.
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
//Initialstate value is an empty array to hold all the colors generated
value: [],
};
//A function to generate random RGB values
const randomRgb = () => {
const red = Math.floor(Math.random() * 256);
const green = Math.floor(Math.random() * 256);
const blue = Math.floor(Math.random() * 256);
return `rgb(${red}, ${green}, ${blue})`;
};
//state slice
export const colorSlice = createSlice({
name: "color",
initialState,
reducers: {
setColor: (state) => {
state.value = [...state.value, randomRgb()];
},
},
});
// Action creators are automatically generated for each case reducer function
export const { setColor } = colorSlice.actions;
export default colorSlice.reducer;
store/colorSlice.js
При написании основной логики Redux мы избегаем прямого изменения значения состояния. Но с помощью Redux Toolkit мы можем писать мутирующий код в редукторах и преобразовывать его в неизменяемые копии.
ПРИМЕЧАНИЕ: Мы можем писать мутирующий код только внутри API createSlice
или createReducer
.
Вы заметите, что мы не определили никаких объектов действия в нашем коде. Это потому, что Redux Toolkit позволяет нам создавать действия на лету. Здесь мы устанавливаем функции case, определенные в нашем редукторе, в colorSlice.actions
. Затем автоматически создается создатель действия, используя имя редуктора в качестве типа действия.
После этого мы можем импортировать и добавить срез в корневой reducer магазина.
...
import colorSlice from "./colorSlice";
export const store = configureStore({
reducer: {
color: colorSlice,
},
});
store/store.js
useSelector и useDispatch
Мы успешно настроили систему Redux для нашего приложения. Теперь все, что нам нужно, это иметь возможность читать текущее состояние в homeScreen.js
и отправлять действие нашему редуктору.
Для этого мы будем использовать хук useSelector
, который даст нам доступ к состоянию нашего redux, и хук useDispatch
, который позволит нам диспетчеризировать действия.
...
import { useDispatch, useSelector } from "react-redux";
import { setColor } from "../../../store/colorSlice";
...
const HomeScreen = () => {
const color = useSelector((state) => state.color.value); //reading the state
const dispatch = useDispatch();
return (
...
<TouchableOpacity
onPress={() => dispatch(setColor())}
...
>
<Text style={{ fontSize: 20 }}>Generate Random Color</Text>
</TouchableOpacity>
<FlatList
data={color}
...
/>
);
};
export default HomeScreen;
...
src/screens/homescreen.js
Мы импортируем useDispatch
и useSelector
из React-Redux, а также созданный нами редуктор setColor
. Захватив наше текущее состояние с помощью state.color.value
, мы устанавливаем его в качестве элемента данных в нашем Flatlist
. Затем, вызвав useDispatch
в качестве диспетчера и передав setColor
в нашем обратном вызове onPress, мы можем отправить действие в соответствующий случай reducer.
Готово! Теперь наше приложение React Native может генерировать случайные цвета.
Когда использовать Redux?
Очевидно, что приложение, которое мы только что создали, слишком простое, чтобы использовать глобальный менеджер состояний, такой как Redux. Это руководство было просто для того, чтобы представить инструментарий Redux самым простым способом.
Так когда же нам следует использовать Redux?
- Когда есть значительный объем данных, изменяющихся во времени.
- Когда нам нужно отслеживать изменения состояния
- Когда мы имеем дело с глубоко вложенными компонентами, и передача состояния и props становится проблематичной
- Когда нескольким компонентам требуется доступ к одному и тому же фрагменту состояния.
Заключение
В этом уроке мы рассмотрели, что такое Redux, основные термины в Redux и Redux Toolkit. Мы также рассмотрели основные шаги по интеграции Redux Toolkit в React Native приложение через
- Создание хранилища с помощью
configureStore
- Предоставление этого хранилища для нашего приложения
- Создание фрагментов редуктора с помощью
createSlice
- и чтение и обновление состояния с помощью
useSelector
иuseDispatch
.
Для дальнейшего изучения я рекомендую обратиться к официальной документации Redux.