Невозможно вызвать setState на отключенном компоненте

Продолжайте получать следующее сообщение об ошибке в React Native, действительно не понимаю, откуда оно

Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

У меня есть такой простой компонент:

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            isLoggedIn: false,
        } 
    }

    componentDidMount(){
        this.fetchToken()
      }

    async fetchToken(){
        const access_token = await AsyncStorage.getItem('access_token')
        if (access_token !== null) {
           this.setState({ isLoggedIn: true })
        }
    }

    render() {
        const login = this.state.isLoggedIn
        if (login) {
            return <NavigatorLoggedIn />
        } else { 
            return <Navigator/>
        }
    }

}

Вы должны инициализировать состояние внутри конструктора, а не за его пределами. И используйте this.state, а не только state. Я также не стал бы применять атрибут async к методам жизненного цикла React по умолчанию, лучше оставить их как есть. Создайте для этого отдельную функцию компонента async/await, затем вызовите ее в componentDidMount.

Jayce444 09.06.2018 16:10

Спасибо за ответ! Обновил компонент с вашими предложениями. Оригинальное предупреждение все еще там; (

Marlow 09.06.2018 16:20
componentDidMount() запускается после рендеринга, поэтому обновленное состояние не отображается / не доступно в render(). Тогда я предполагаю, что этот компонент в любом случае размонтируется через render(), и поэтому вызов fetchToken() после факта вызывает утечку памяти в соответствии с сообщением об ошибке.
radarbob 09.06.2018 16:56

@radarbob так думал! есть предложения по решению? Сам пытаюсь найти решение

Marlow 09.06.2018 17:22

Вне руки: (1) сделайте это в render() на самом верху. (2) сделайте это в конструкторе. В любом случае используйте другая форма setState(), который принимает функцию; потому что setState() принципиально асинхронный

radarbob 09.06.2018 20:59

Возможный дубликат Предупреждение реакции о setState в отключенном компоненте

fkoessler 12.09.2018 14:24
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
6
9 987
6

Ответы 6

Вы можете использовать это:

componentDidMount() {
    this.function()
}

function = async () => { 
    const access_token = await AsyncStorage.getItem('access_token')
    if (access_token !== null) {
        this.setState({ isLoggedIn: true })
    }
}

Или вы можете вызвать в function в constructor.

Я надеюсь, что это поможет вам...

Спасибо за быстрый ответ! Пробовал, но получил такое же предупреждение в исходном вопросе

Marlow 09.06.2018 15:43

Обратите внимание: не вызывайте функции async / await в конструкторе. Конструктор должен инициализировать компонент и вернуть его как можно скорее. Если вам нужно дождаться асинхронной функции, вызовите ее в componentDidMount.

Jayce444 09.06.2018 16:06

вам нужно использовать переменную isMounted.

componentDidMount(){
  this.setState({ isMounted = true });
  const access_token = await AsyncStorage.getItem('access_token')
  if (access_token !== null && this.isMounted) {
     this.setState({ isLoggedIn: true })
  }
}

componentWillUnmount(){
   this.setState({ isMounted = false });
}

Или, если вы используете Axios, вы можете использовать функцию запроса отмены axios. это здесь: https://github.com/axios/axios#cancellation

Спасибо за быстрый ответ! Пробовал, но setState isLoggedIn не работает таким образом

Marlow 09.06.2018 15:44

Я не понимаю. Если компонент все еще смонтирован, setState работает нормально.

san 09.06.2018 15:53

оператор if не запускается, && this.isMounted не работает

Marlow 09.06.2018 16:07

Вы пытались использовать обещание или обратный вызов вместо async / await?

san 10.06.2018 03:50

Документы RN говорят, что не следует вызывать setState при отключении: reactjs.org/docs/react-component.html#componentwillunmount

mheavers 26.11.2018 02:23

Будет работать:

let self;

class App extends React.Component {
    constructor(props) {
        super(props)
        self = this;
        this.state = {
            isLoggedIn: false,
        } 
    }

    componentDidMount(){
        this.fetchToken()
      }

    async fetchToken(){
        const access_token = await AsyncStorage.getItem('access_token')
        if (access_token !== null) {
           self.setState({ isLoggedIn: true })
        }
    }

    render() {
        const login = self.state.isLoggedIn
        if (login) {
            return <NavigatorLoggedIn />
        } else { 
            return <Navigator/>
        }
    }

}

Для меня я решил это, перезапустив сервер с помощью "yarn start" или "npm start".

Вы можете попробовать это:

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            isLoggedIn: false,
        } 
    }

    _isMounted = false;   

    componentDidMount(){
        this._isMounted = true; 
        this.fetchToken()
      }

    async fetchToken(){
        const access_token = await AsyncStorage.getItem('access_token')
        if (access_token !== null && this._isMounted) {
           this.setState({ isLoggedIn: true })
        }
    }

    componentWillUnmount() {
        this._isMounted = false;
    } 

    render() {
        const login = this.state.isLoggedIn
        if (login) {
            return <NavigatorLoggedIn />
        } else { 
            return <Navigator/>
        }
    }

}

При использовании _isMounted setState вызывается только в том случае, если компонент смонтирован. Размонтирование не дожидается завершения асинхронного вызова. В конце концов, когда асинхронный вызов завершается, компонент уже размонтирован и поэтому не может установить состояние. Для этого ответ просто проверяет, смонтирован ли компонент, перед установкой состояния.

Отмена всех асинхронных операций - одно из решений

Другие вопросы по теме