【问题标题】:`componentDidMount()` function is not called after navigation导航后不调用`componentDidMount()`函数
【发布时间】:2018-06-09 15:03:56
【问题描述】:

我正在使用stackNavigator 在屏幕之间导航。在我的第二个活动中,我在 componentDidMount() 函数中调用了两个 API。当我第一次加载它时,它会成功加载。然后我按返回按钮返回第一个活动。然后,如果我再次进行第二个活动,则不会调用 API,并且会出现渲染错误。我无法为此找到任何解决方案。任何建议将不胜感激。

【问题讨论】:

  • componentDidMount 在其生命周期内仅被调用一次。请检查componentWillReceiveProps 方法
  • 如果您返回navigate.goBack(),当您再次使用navigate 时,componentDidMount() 应该会再次发生。 (刚刚在我的代码中确认)。你有没有用特殊的方式处理回去?建议用更简单的代码试试看。
  • 我没有以特殊方式处理goBack()。我正在按下屏幕顶部的后退箭头按钮。

标签: android reactjs react-native stack-navigator


【解决方案1】:

我遇到过这个问题,问题是当你导航一个页面时,第一次调用构造函数,componentWillmount,render componentDidmount, 但是在第二次导航到同一页面时它只调用渲染,所以如果你从 componentDidmount 进行任何 API 调用或其他操作,它不会被调用,

还有 componentWillunmount 从未调用过。

你可以使用这个方法,如果你使用 react-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;
        });
    }

【讨论】:

    【解决方案2】:

    https://reactnavigation.org/docs/navigation-events/

    useEffect(() => {       
                
        const unsubscribe = props.navigation.addListener('focus', () => {
            // do something
            // Your apiCall();
        });
    
    return unsubscribe;
        
    }, [props.navigation]);
    

    【讨论】:

      【解决方案3】:

      2020 年解决方案 / React Navigation v5

      在屏幕的 ComponentDidMount 中

      componentDidMount() {
          this.props.navigation.addListener('focus', () => {
            console.log('Screen.js focused')
          });
      }
      

      https://reactnavigation.org/docs/navigation-events/

      或者:将 addListener 方法放在构造函数中,以防止重复调用

      【讨论】:

        【解决方案4】:
        //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>
        );
        }
        

        【讨论】:

        • 希望它能解决问题,但请添加对代码的解释,以便用户完全理解他/她真正想要的。
        • 也.. 真的用母语编写代码吗?所以 1990 年代 ;-)
        【解决方案5】:

        根据react-navigation docs我们可以如下使用

        componentDidMount () {
          this.unsubscribe= this.props.navigation.addListener('focus', () => {
            //Will execute when screen is focused
          })
        }
        
        componentWillUnmount () {
          this.unsubscribe()
        }
        

        与 vitosorriso 的回答类似,但应根据文档将 didFocus 更改为焦点

        【讨论】:

          【解决方案6】:

          React-navigation 文档explicitly described this case

          考虑一个带有屏幕 A 和 B 的堆栈导航器。导航到 A、它的componentDidMount被调用。当推 B 时,它的 componentDidMount 也被调用,但 A 仍然安装在堆栈上 因此它的 componentWillUnmount 不会被调用。

          从B回到A时,B的componentWillUnmount被调用,但是 A 的 componentDidMount 不是因为 A 保持整个挂载 时间。

          现在有 3 个解决方案:

          订阅生命周期事件

          ...

          React Navigation 向订阅的屏幕组件发出事件 他们。您可以订阅四种不同的事件: willFocuswillBlurdidFocusdidBlur。在 API 参考。

          withNavigationFocus 可能涵盖您的许多用例 高阶组件、&lt;NavigationEvents /&gt; 组件或 useFocusState钩子。

          1. withNavigationFocus higher-order-component
          2. &lt;NavigationEvents /&gt; component
          3. useFocusState hook已弃用

          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);
          

          &lt;NavigationEvents /&gt; 组件

          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>; }
          

          这是我的个人解决方案,仅在从您的 API 获取数据时才使用 onDidFocus()onWillFocus() 呈现:

          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;
          

          【讨论】:

            【解决方案7】:

            如果使用 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()
            }
            

            【讨论】:

              【解决方案8】:

              如果有人在 2019 年来到这里,试试这个:

              import {NavigationEvents} from 'react-navigation';
              

              将组件添加到您的渲染中:

              <NavigationEvents onDidFocus={() => console.log('I am triggered')} />
              

              现在,尽管来自 goBack() 或导航,但每次页面获得焦点时都会触发此 onDidFocus 事件。

              【讨论】:

              • 我建议 onWillFocus,因为它不会造成任何延迟。焦点是否可能会导致用户可见的轻微延迟。
              • 没有人在为我工作 onDidFocus 和 onWillFocus 。我正在使用 createDrawerNavigator。
              • 在哪里使用 NavigationEvents?它给了我一个错误“withnavigation 只能用于导航器的视图层次结构”
              【解决方案9】:

              您希望在每次使用 drawernavigatorstacknavigator 导航到组件后执行某些操作;为此,NavigationEventscomponentDidMount 更适合。

              import {NavigationEvents} from "react-navigation";
              <NavigationEvents onDidFocus={()=>alert("Hello, I'm focused!")} />
              

              注意:如果您想在每次使用抽屉导航或堆栈导航关注组件后执行任务,那么使用NavigationEvents 是更好的主意。但是,如果您想执行一次任务,则需要使用 componenetDidMount

              【讨论】:

                【解决方案10】:

                即使您在屏幕之间导航,React-navigation 也会保持组件挂载。您可以使用该组件对这些事件做出反应:

                <NavigationEvents
                  onDidFocus={() => console.log('hello world')}
                />
                

                关于这个组件的更多信息here

                【讨论】:

                  【解决方案11】:

                  在 React 中,仅在安装组件时才调用 componentDidMount。我认为您要做的是在返回 StackNavigator 时调用您的 API。当您在父屏幕上调用导航时,您可以将回调函数作为参数传递:

                    navigate("Screen", {
                       onNavigateBack: this.handleOnNavigateBack
                    }); 
                    handleOnNavigateBack = () => {//do something};
                  

                  在子屏幕上

                  this.props.navigation.state.params.onNavigateBack();
                  this.props.navigation.goBack();
                  

                  【讨论】:

                  • 感谢@suyog 的回复。。有没有什么方法可以让文档在所有API都被调用后才呈现。
                  • 在您的渲染方法中检查 (!prop1FromApi || !prop2FromApi) 是否返回 null;
                  • 您可以设置活动的状态,例如 loading : false 并在加载为 false 时在渲染函数中显示加载图标。然后可以在调用api后设置loading为true。
                  猜你喜欢
                  • 2018-12-09
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-03-31
                  • 2019-06-07
                  • 2019-01-23
                  • 1970-01-01
                  • 2020-06-09
                  • 1970-01-01
                  相关资源
                  最近更新 更多