Функция `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 крючок.

  1. в withNavigationFocusкомпонент высшего порядка
  2. в <NavigationEvents />составная часть
  3. 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();
Другие вопросы по тегам