Не удается установить состояние в componentWillMount

#javascript #reactjs #state

#javascript #reactjs #состояние

Вопрос:

Я создаю простое приложение для чата, в котором я вызываю api к своей базе данных через axios, который возвращает массив объектов сообщений. Я могу получить данные, когда я выполняю вызов axios в componentWillMount. Затем я пытаюсь setState отобразить диалог. Вот код:

 export default class Chat extends Component {
  constructor(props){
    super(props);

    this.state = {
      messages : [],
      message : '',
    };
    this.socket = io('/api/');
    this.onSubmitMessage = this.onSubmitMessage.bind(this);
    this.onInputChange = this.onInputChange.bind(this);
  }

  componentWillMount() {
    axios.get(`api/messages`)
      .then((result) => {
        const messages = result.data
        console.log("COMPONENT WILL Mount messages : ", messages);
        this.setState({ 
          messages: [ ...messages.content ] 
        })
  })
};
  

Я видел несколько сообщений, касающихся функций жизненного цикла и настройки состояния, и кажется, что я поступаю правильно.

Опять же, чтобы подчеркнуть, вызов axios работает нормально, настройка состояния не работает. Я все еще вижу пустой массив. Заранее спасибо!

РЕДАКТИРОВАТЬ: Вот решение конкретно моей проблемы. Это было скрыто в комментарии, поэтому я подумал, что оставлю это здесь..

«Я обнаружил проблему. На самом деле это было в том, как я разбирал свои данные. Оператор распространения включен…messages.content не сработал, потому что messages.content не существует. сообщения [i]. содержимое существует. Итак, мое исправление состояло в том, чтобы просто распространять…сообщения Затем в дочернем компоненте я сопоставляю объекты и анализирую свойство .content. Спасибо за помощь, ребята!»

Ответ №1:

В вашем случае ваш setState() не будет работать, потому что вы используете setState() внутри асинхронного обратного вызова

Рабочая скрипка:https://jsfiddle.net/xytma20g/3 /

Вы выполняете асинхронный вызов API. Таким образом, setState будет вызван только после получения данных. Это ничего не делает с componentWillMount или componentDidMount . Вам нужно обработать пустое значение message при рендеринге. Когда вы получите свои данные из API, установите для этих данных значение state, и компонент повторно отобразит новое состояние, которое будет отражено в вашем рендеринге.

Псевдокод:

 export default class Chat extends Component {
  constructor(props){
    super(props);

    this.state = {
      messages : [],
      message : '',
    };
    this.socket = io('/api/');
    this.onSubmitMessage = this.onSubmitMessage.bind(this);
    this.onInputChange = this.onInputChange.bind(this);
  }

  componentWillMount() {
    axios.get(`api/messages`)
      .then((result) => {
        const messages = result.data
        console.log("COMPONENT WILL Mount messages : ", messages);
        this.setState({ 
          messages: [ ...messages.content ] 
        })
  })

  render(){
    if(this.state.messages.length === 0){
     return false //return false or a <Loader/> when you don't have anything in your message[]
    }

   //rest of your render.
  }
}; 
  

Комментарии:

1. Я пытаюсь возобновить диалог, когда делаю запрос get, чтобы диалог сохранялся при перезагрузке. Кажется, что загрузчик выдаст мне запрос «нет новых сообщений» и отобразит только новые сообщения, которые я ввожу оттуда. Я также думал о том, чтобы сделать запрос GET в компоненте более высокого уровня, чтобы сообщения отображались в состоянии. Вот как теперь выглядит мой метод рендеринга

2. Я обнаружил проблему. На самом деле это было в том, как я разбирал свои данные. Оператор распространения включен… messages.content не сработал, потому что messages.content не существует. сообщения [i]. содержимое существует. Итак, мое исправление состояло в том, чтобы просто распространять… сообщения Затем в дочернем компоненте я сопоставляю объекты и анализирую свойство .content. Спасибо за помощь, ребята!

3. @PhilSeidel Ты молодец. Продолжай в том же духе.

Ответ №2:

componentWillMount() вызывается непосредственно перед началом монтирования. Он вызывается перед render(), поэтому установка состояния в этом методе не вызовет повторной визуализации. Избегайте введения каких-либо побочных эффектов или подписок в этом методе. Документы

Итак, вам нужно вызвать componentDidMount как-

 componentDidMount() {
    axios.get(`api/messages`)
      .then((result) => {
        const messages = result.data
        console.log("COMPONENT WILL Mount messages : ", messages);
        this.setState({ 
          messages: [ ...messages.content ] 
        })
  })
  

Комментарии:

1. Ваша точка верна. Но React будет использовать новое значение состояния для первого рендеринга вместо повторного рендеринга. В этом смысл инструкции метод не будет запускать повторный рендеринг . Для вашего пояснения я добавляю рабочий пример. jsfiddle.net/q25sfL0z

2. что, если ваш вызов API занял слишком много времени, чтобы вернуть ответ?

3. В этом случае вам следует использовать загрузчик для указания пользователя.

4. Я улавливаю, что начальный рендеринг и повторный рендеринг отличаются. В противном случае ваша точка зрения верна!