Функция `componentDidMount()` не вызывается после навигации
Я использую stackNavigator
для навигации между экранами. Я звоню двум API в componentDidMount()
функция в моей второй деятельности. Когда я загружаю его в первый раз, он загружается успешно. Затем я нажимаю кнопку "Назад", чтобы вернуться к первому занятию. Затем, если я снова перехожу ко второму действию, API не вызываются, и я получаю ошибку рендеринга. Я не могу найти никакого решения для этого. Мы ценим любые предложения.
11 ответов
Если кто-нибудь приедет сюда в 2018 году, попробуйте это:
import {NavigationEvents} from 'react-navigation';
Добавьте компонент в ваш рендер:
<NavigationEvents onDidFocus={() => console.log('I am triggered')} />
Теперь это событие onDidFocus будет вызываться каждый раз, когда страница фокусируется, несмотря на то, что она поступила из goBack() или навигации.
Если синтаксис с голосованием, использующий компонент NavigationEvents, не работает, вы можете попробовать это:
// no need to import anything more
// define a separate function to get triggered on focus
onFocusFunction = () => {
// do some stuff on every screen focus
}
// add a focus listener onDidMount
async componentDidMount () {
this.focusListener = this.props.navigation.addListener('didFocus', () => {
this.onFocusFunction()
})
}
// and don't forget to remove the listener
componentWillUnmount () {
this.focusListener.remove()
}
В документации по React-навигации этот случай подробно описан:
Рассмотрим навигатор по стеку с экранами A и B. После перехода к A вызывается его componentDidMount. При нажатии B также вызывается его componentDidMount, но A остается смонтированным в стеке, поэтому его componentWillUnmount не вызывается.
При возврате от B к A вызывается componentWillUnmount из B, но componentDidMount из A не вызывается, потому что A все время оставался смонтированным.
Теперь для этого есть 3 решения:
Подписка на события жизненного цикла
...
React Navigation отправляет события компонентам экрана, которые на них подписаны. Вы можете подписаться на четыре разных события:
willFocus
,willBlur
,didFocus
а такжеdidBlur
. Подробнее о них читайте в справочнике по API.Многие из ваших вариантов использования могут быть охвачены
withNavigationFocus
компонента более высокого порядка,<NavigationEvents />
компонент, илиuseFocusState
крючок.
- в
withNavigationFocus
компонент высшего порядка - в
<NavigationEvents />
составная часть - useFocusState крюк (нерекомендуется)
withNavigationFocus
компонент высшего порядка
import React from 'react';
import { Text } from 'react-native';
import { withNavigationFocus } from 'react-navigation';
class FocusStateLabel extends React.Component {
render() {
return <Text>{this.props.isFocused ? 'Focused' : 'Not focused'}</Text>;
}
}
// withNavigationFocus returns a component that wraps FocusStateLabel and passes
// in the navigation prop
export default withNavigationFocus(FocusStateLabel);
<NavigationEvents />
составная часть
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus', payload)}
onDidFocus={payload => console.log('did focus', payload)}
onWillBlur={payload => console.log('will blur', payload)}
onDidBlur={payload => console.log('did blur', payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;
useFocusState хук
Библиотека первой установки yarn add react-navigation-hooks
import { useNavigation, useNavigationParam, ... } from 'react-navigation-hooks'
function MyScreen() { const focusState = useFocusState(); return <Text>{focusState.isFocused ? 'Focused' : 'Not Focused'}</Text>; }
Вот мое личное решение, использующее onDidFocus()
а также onWillFocus()
для рендеринга только тогда, когда данные были получены из вашего API:
import React, { PureComponent } from "react";
import { View } from "react-native";
import { NavigationEvents } from "react-navigation";
class MyScreen extends PureComponent {
state = {
loading: true
};
componentDidMount() {
this._doApiCall();
}
_doApiCall = () => {
myApiCall().then(() => {
/* Do whatever you need */
}).finally(() => this.setState({loading: false}));
};
render() {
return (
<View>
<NavigationEvents
onDidFocus={this._doApiCall}
onWillFocus={() => this.setState({loading: true})}
/>
{!this.state.loading && /*
Your view code
*/}
</View>
);
}
}
export default MyScreen;
Решение для 2020 / React Navigation v5
Внутри вашего экрана ComponentDidMount
componentDidMount() {
this.props.navigation.addListener('focus', () => {
console.log('Screen.js focused')
});
}
https://reactnavigation.org/docs/navigation-events/
В качестве альтернативы: вместо этого поместите метод addListener в конструктор, чтобы предотвратить дублирование вызовов.
React-навигация сохраняет компонент подключенным, даже если вы перемещаетесь между экранами. Вы можете использовать компонент, чтобы реагировать на эти события:
<NavigationEvents
onDidFocus={() => console.log('hello world')}
/>
Больше информации об этом компоненте здесь.
Согласно документации по реагированию на навигацию, мы можем использовать, как показано ниже
componentDidMount () {
this.unsubscribe= this.props.navigation.addListener('focus', () => {
//Will execute when screen is focused
})
}
componentWillUnmount () {
this.unsubscribe()
}
Подобно ответу vitosorriso, но следует изменить didFocus на фокус в соответствии с документами
Вы хотите выполнить что-то после каждого перехода к компоненту с помощью drawernavigator
или же stacknavigator
; для этой цели NavigationEvents
подходит лучше, чем componentDidMount
,
import {NavigationEvents} from "react-navigation";
<NavigationEvents onDidFocus={()=>alert("Hello, I'm focused!")} />
Примечание. Если вы хотите выполнять задачу каждый раз после фокусировки на компоненте с помощью навигации по ящикам или стековой навигации, используйте NavigationEvents
это лучшая идея. Но если вы хотите выполнить задачу один раз, то вам нужно использовать componenetDidMount
,
//na pagina que você quer voltar
import {NavigationEvents} from 'react-navigation';
async atualizarEstado() {
this.props.navigation.setParams({
number: await AsyncStorage.getItem('count'),
});}
render() {
return (
<View style={styles.container}>
<NavigationEvents onDidFocus={() => this.atualizarEstado()} />
</View>
);
}
Я столкнулся с этой проблемой, проблема заключается в том, что когда вы перемещаетесь по странице, в первый раз он вызывает конструктор, componentWillmount, отображает componentDidmount, но во второй раз, когда переходите на ту же страницу, он вызывает только визуализацию, поэтому, если вы выполняете какой-либо вызов API или что-то из componentDidmount не будет вызываться,
а также componentWillunmount никогда не вызывался.
Вы можете использовать этот метод, если вы используете response-navigation 5.x с компонентом класса, он может решить вашу проблему.
для каждой страницы компонента класса добавьте этот метод и вызовите этот метод один раз из конструктора
constructor(props) {
super(props);
this.state = {
...
};
...
this.navigationEventListener(); // call the function
}
navigationEventListener = () => { // add this function
let i = 0;
const initialState = this.state;
this.props.navigation.addListener('focus', () => {
if (i > 0) {
this.setState(initialState, () => {
//this.UNSAFE_componentWillMount(); // call componentWillMount
this.componentDidMount(); // call componentDidMount
});
}
});
this.props.navigation.addListener('blur', () => {
this.componentWillUnmount(); //call componentWillUnmount
++i;
});
}
https://reactnavigation.org/docs/navigation-events/
useEffect(() => {
const unsubscribe = props.navigation.addListener('focus', () => {
// do something
// Your apiCall();
});
return unsubscribe;
}, [props.navigation]);
В React componentDidMount вызывается только тогда, когда компонент смонтирован. Я думаю, что вы пытаетесь сделать это - вызвать свой API при возвращении в StackNavigator. Вы можете передать функцию обратного вызова в качестве параметра при вызове навигации, как это на родительском экране:
navigate("Screen", {
onNavigateBack: this.handleOnNavigateBack
});
handleOnNavigateBack = () => {//do something};
И на экране ребенка
this.props.navigation.state.params.onNavigateBack();
this.props.navigation.goBack();