#react-native #react-hooks #react-native-navigation
#реагировать-родной #реагирующие хуки #реагировать на встроенную навигацию #react-native #react-native-navigation
Вопрос:
В моем проекте react native у меня есть три флажка, мне нужно отслеживать состояние этих флажков, поэтому я использую объект с ключом-значением (значение логическое) для представления состояний всех трех флажков и использую useState
hook для управления ими. Вот мой код:
import React, { useState, useEffect } from 'react';
...
const MyScreen = ({ navigation }) => {
// initially, all checkboxes are checked
const initialCheckBoxState = {
0: true,
1: true,
2: true,
};
const [checkBoxesState, setCheckBoxesState] = useState(initialCheckBoxState);
useEffect(() => {
return () => {
console.log('Screen did unmount');
};
}, [checkBoxesState]);
return (
<View>
...
<SectionList
sections={options}
renderItem={({ index, item }) => (
<CheckBox
onPress={() => {
const checkBoxesStateCopy = { ...checkBoxesState };
checkBoxesStateCopy[index] = !checkBoxesStateCopy[index];
setCheckBoxesState(checkBoxesStateCopy);
}}
/>
)}
/>
...
</View>
);
};
Я опустил код, который не касается моей проблемы. Как вы можете видеть, для каждого элемента я рисую один CheckBox
компонент.
На практике всегда есть три элемента (т. Е. Три флажка для отображения). В начале я объявил initialCheckBoxState
, что каждая пара ключей представляет состояние флажка каждого. При onPress
обратном вызове Checkbox
я переключаю состояние каждого флажка и обновляю checkBoxesState
метод hook setCheckBoxesState
в целом.
Во время выполнения все работает нормально, мой экран повторно отображается при переключении состояния флажка, пользовательский интерфейс правильно отображает состояние флажков. Но проблема возникает, когда я возвращаюсь к предыдущему экрану и возвращаюсь к этому экрану, все состояния флажков возвращаются к исходным состояниям.
Итак, почему состояния флажков не зарезервированы?
P.S. предыдущий экран и MyScreen
находятся в одном и том же навигаторе стека. Пользователь нажимает кнопку предыдущего экрана, чтобы перейти к MyScreen
. С MyScreen
пользователь может перейти к предыдущему экрану, нажав кнопку «Заголовок слева»
Комментарии:
1. поскольку здесь не указана нужная мне часть кода, можете ли вы просто поставить
console.log('unmounted')
вcomponentWillUnmount
на странице, на которой вы находитесь, при нажатии кнопки «Назад». просто скажите мне, видите ли вы этот журнал или нет в консоли2. вы можете передавать
checkBoxesState
иsetCheckBoxesState
в качестве реквизита вMyScreen
и можете вызывать изMyScreen
, а для сохранения состояния вы можете использовать github.com/leoafarias/use-state-persist этот шаг3. @Amas, я обновил свой фрагмент кода, теперь у меня есть журнал консоли, чтобы проверить, размонтирован ли компонент. Я тестировал, я вижу этот консольный журнал каждый раз, когда я покидаю экран. Но почему это так? Насколько я понимаю, экран некоторое время будет оставаться в заднем стеке памяти, но, похоже, экран немедленно отключается. В любом случае, как решить мою проблему?
Ответ №1:
Сначала давайте ответим на вопрос:
почему состояния флажков не зарезервированы?
Этот компонент обрабатывает свое состояние полностью независимо, состояние создается и обрабатывается внутри, и никакие значения не передаются извне. что это значит? этот компонент имеет свое начальное значение состояния внутри себя, он не использует какой-либо реквизит или что-либо еще для инициализации состояния. каждый раз, когда создается этот компонент, состояние снова инициализируется этим значением. так что это причина, по которой вы теряете все изменения, внесенные в checkboxes
, потому что, когда вы покидаете этот экран (компонент) , он отключается (мы поговорим об этом в следующем вопросе), и поскольку все значения обрабатываются только внутри, все данные (содержащие состояние флажков) будут потеряны.
Итак, теперь давайте поговорим об этом:
предполагается ли, что react-native резервирует состояние при возвращении на экран?
короткий ответ — нет. Каждый компонент уничтожается при отключении, включая их состояние и данные.
Теперь давайте ответим, почему
экраны все еще находятся в стеке в памяти, не уничтожены?
Обычно разработчики используют пакет типа react-navigation
or RNRF
(который построен поверх react-navigation
) для навигации react, в большинстве случаев нас не волнует, как они обрабатывают эту логику навигации, мы просто используем интерфейс, предоставленный нам. каждый из этих пакетов может иметь свой собственный способ управления навигацией. предоставление полного ответа, чтобы определить, почему именно экран, который все еще находится в памяти, нуждается в полном просмотре кода и, конечно, в большой отладке, но я предполагаю, что есть 2 возможности. во-первых, как я уже сказал, возможно, используемый вами пакет по какой-то причине сохраняет размонтированные экраны в памяти хотя бы на некоторое время. 2-я — распространенная проблема react
сообщества, которую Unmounted component still in memory
вы можете проверить по адресу:https://github.com/facebook/react/issues/16138
И наконец давайте ответим на вопрос:
как мне сохранить состояние флажков даже при переходе назад и потере компонента, содержащего их состояние?
У этого нет только одного способа, но простой и короткий ответ — переместить ваше состояние из этого компонента, например, переместить его в родительский компонент или глобальную переменную. чтобы сделать это более понятным, давайте объясним так: представьте, что экран A
всегда смонтирован, затем вы заходите B
, и там вы можете увидеть некоторые флажки, и вы можете изменять состояния. если состояние обрабатывается полностью внутри B
, при обратном переходе с экрана B
на A
, вы теряете все изменения, потому что B
теперь оно размонтировано. итак, что вы должны сделать, чтобы установить флажки на A
экране, а затем передать значения вниз B
. и при изменении значений вы изменяете A
состояние. поэтому, когда B
размонтируется, все изменения сохраняются, потому что они у вас есть A
.
существует и другой подход, вы можете создать глобальный одноэлементный объект с именем globalState
. затем поместите туда значения, необходимые для совместного использования между несколькими экранами. если вы предпочитаете redux
или mobx
, вы можете их использовать. одно из их применений — когда у вас есть некоторые данные, которые вам нужно разделить между несколькими экранами, эти данные не зависят от того, где вы находитесь, и будут сохраняться.
Комментарии:
1. Примечание: AsyncStorage обычно используется для хранения данных, подобных
token
в приложении, а НЕ для сохранения состояния2.
AsyncStorage
именно для сохранения состояния причина, по которой AsyncStorage необходим просто потому, что по какой-либо причине,state
требуется даже после перезапуска приложения, поэтому делать вывод, чтоAsyncStorage
просто хранить данные, подобныеtoken
, неуместно3. @Isaac хотя существует множество способов сохранения состояния, я признаю, что я не рассматривал сценарий перезапуска и повторного запуска приложения и говорил только об обработке состояния в целом. для этого конкретного вопроса использование асинхронного хранилища не только не рекомендуется, но и является плохой практикой. но в целом да, stringify и синтаксический анализ объектов для сохранения данных распространены, но не этот
Ответ №2:
Это объяснение взято из официальной react-navigation
документации:
Рассмотрим навигатор стека с экранами A и B. После перехода к A вызывается его componentDidMount . При нажатии кнопки B также вызывается его componentDidMount, но A остается смонтированным в стеке, и поэтому его componentWillUnmount не вызывается.
При возврате из B в A вызывается componentWillUnmount из B, но componentDidMount из A не потому, что A оставался установленным все время.
https://reactnavigation.org/docs/navigation-lifecycle/#example-scenario
Ваш экран MyScreen эквивалентен экрану B из примера, что означает, что вы можете ожидать, что ваш экран останется подключенным, если вы перемещаетесь вперед, но не назад.
Ответ №3:
Это просто, просто добавьте keyExtractor в свой компонент SectionList, который будет однозначно идентифицировать каждый флажок, чтобы react знал, какой из них повторно отображать при обновлении.
Ответ №4:
Вы захотите использовать AsyncStorage для сохранения данных на устройстве. Переменные состояния будут очищаться при каждом отключении компонента.
AsyncStorage docs:https://react-native-community.github.io/asaync-storage
import AsyncStorage from '@react-native-community/async-storage';
//You can only store string values so convert objects to strings:
const storeData = async (value) => {
try {
const jsonValue = JSON.stringify(value)
await AsyncStorage.setItem('@storage_Key', jsonValue)
} catch (e) {
// saving error
}
}
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('@storage_Key')
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch(e) {
// error reading value
}
}
Обновить —
Состояние не сохраняется из-за характера жизненного цикла компонента React. В частности, при переходе с экрана вызывается метод жизненного цикла componentWillUnmount
.
Вот выдержка из документов:
componentWillUnmount() вызывается непосредственно перед размонтированием и уничтожением компонента. Выполните любую необходимую очистку в этом методе, например, аннулируйте таймеры, отмените сетевые запросы или очистите все подписки, которые были созданы в componentDidMount().…Как только экземпляр компонента размонтирован, он никогда не будет смонтирован снова.
Это означает, что любые значения, сохраненные в состоянии, также будут уничтожены, и при переходе обратно на экран ComponentDidMount
будет вызван, где вы можете присвоить сохраненные значения обратно в состояние.
Два возможных подхода, помимо AsyncStorage, которые могут сработать в некоторых случаях использования для сохранения данных на экранах, — это использование контекста или синглтона.
Комментарии:
1. Нет, я не знаю. Моя проблема в том, что оба экрана все еще находятся в памяти, каждый раз, когда состояние сбрасывается на исходное при возвращении к экрану, но предполагается, что react-native резервирует состояние при возвращении к экрану. Я хочу понять, почему бы просто не переключиться на AsyncStorage, не понимая проблемы, с которой я сталкиваюсь. Но спасибо вам за ваш ответ.
2. Я добавил обновление, надеюсь, это поможет прояснить ситуацию.
3. Спасибо, но, если я правильно понимаю ваш обновленный ответ, по-прежнему предполагается, что компонент как размонтирован, так и уничтожен. Как я уже сказал, в моем случае оба экрана все еще находятся в стеке памяти, а не уничтожены. Предполагается, что состояние должно быть зарезервировано.