Как получить доступ к обновленному состоянию в теле функции, которая уже вызвала setState? - PullRequest
3 голосов
/ 11 июня 2019

В настоящее время я кодирую функцию для сохранения объекта blogPost в document в моей базе данных Firestore.

Предполагаемая процедура должна быть:

  • Нажмите наКнопка SAVE и вызов функции savePost()
  • Функция savePost() должна вызывать функцию uploadImagesAndGetURL(), потому что изображения blogPost должны быть загружены в Firebase Storage до того, как я сохраню их src URL-адрес вПожарный магазин.(в моем реальном случае загрузка изображения async, и я await для его завершения, но я не думаю, что это повредит приведенному здесь примеру).
  • uploadImagesAndGetURL() должен загрузить изображения исохранить возвращенные значения urls для каждого из них в соответствующем свойстве src внутри массива elements state.
  • Это выполняется путем запуска forEach() в массиве elements и вызова setState() (setBlogPost) для обновления состояния новыми URL-адресами, полученными из хранилища.
  • * 1031Затем будет вызвана функция *, и она сможет получить доступ к самому последнему состоянию, чтобы иметь возможность сохранить объект blogPost вместе с изображением src's в базе данных.

ВОПРОС

Моя проблема в том, что во время выполнения функции savePost() компонент в этом случае перерисовывается 3 раза (для 3 изображений), поскольку uploadImagesAndGetURL() вызывает setBlogPost() три раза.Один для каждого изображения.И когда я достигаю функции saveToFirestore() и регистрирую состояние blogPost, вы видите, что изменилось только первое изображение src.

Я правильно делаю?Как я могу улучшить эту процедуру, чтобы убедиться, что все было обновлено до I saveToFirestore()?

ПРИМЕЧАНИЕ: Один из способов сделать это - пропустить обновление состояния и передать возвращенные URL-адреса прямо в функцию saveToFirestore(), не сохраняя их в состоянии.Но что является подходящим шаблоном в этой ситуации?

Журнал из функции saveToFirestore():

{
  "title": "This is the title",
  "body": "Click on the button to upload this post",
  "elements": [
    {
      "type": "image",
      "src": "source0"
    },
    {
      "type": "image",
      "src": null
    },
    {
      "type": "image",
      "src": null
    }
  ]
}

SNIPPET НИЖЕ

function App() {
  console.log('Rendering App...');
  const [blogPost,setBlogPost] = React.useState({
    title: 'This is the title',
    body: 'Click on the button to upload this post',
    elements: [
      { type: 'image',
        src: null 
      },
      { type: 'image',
        src: null
      },
      { type: 'image',
        src: null 
      }
    ]
  });

  function savePost() {
    console.log('Inside savePost');
    uploadImagesAndGetURL();
    saveToFirestore();
  }
  
  function uploadImagesAndGetURL() {
    console.log('Inside uploadImages');
    blogPost.elements.forEach((item,index) => {
      console.log('Inside uploadImages - forEach');
      setBlogPost((prevState)=>{
        const aux = Array.from(prevState.elements);
        aux[index].src = 'source' + index;
        return({
          ...prevState,
          elements: aux
        });
      });
    });
  }
  
  function saveToFirestore() {
    console.log('Inside saveToFirestore');
    console.log('Will save the following blogPost to Firestore');
    console.log(blogPost);
  }

  return(
    <React.Fragment>
      <div>{blogPost.title}</div>
      <div>{blogPost.body}</div>
      <div>Image 1 src: {blogPost.elements[0].src}</div>
      <div>Image 2 src: {blogPost.elements[1].src}</div>
      <div>Image 3 src: {blogPost.elements[2].src}</div>
      <button onClick={savePost}>Save Post</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

1 Ответ

1 голос
/ 11 июня 2019

Вы можете перемещать логику сохранения в хуке useEffect при изменении состояния блога, это приведет к запуску эффекта.

Следуйте инструкциям Edit so.answer.56544887

import React, { useEffect } from "react";
import ReactDOM from "react-dom";

function App() {
  console.log("Rendering App...");
  const [blogPost, setBlogPost] = React.useState({
    title: "This is the title",
    body: "Click on the button to upload this post",
    elements: [
      { type: "image", src: null },
      { type: "image", src: null },
      { type: "image", src: null }
    ]
  });

  function savePost() {
    console.log("Inside savePost");
    uploadImagesAndGetURL();
    // saveToFirestore();
  }

  function uploadImagesAndGetURL() {
    console.log("Inside uploadImages");
    const elements = blogPost.elements.map((_, index) => ({
      type: "image",
      src: `source${index}`
    }));
    setBlogPost(previousPost => ({ ...previousPost, elements }));
  }

  // function uploadImagesAndGetURL() {
  //   console.log("Inside uploadImages");
  //   blogPost.elements.forEach((item, index) => {
  //     console.log("Inside uploadImages - forEach");
  //     setBlogPost(prevState => {
  //       const aux = Array.from(prevState.elements);
  //       aux[index].src = "source" + index;
  //       return {
  //         ...prevState,
  //         elements: aux
  //       };
  //     });
  //   });
  // }

  useEffect(() => {
    function saveToFirestore() {
      console.log("Inside saveToFirestore");
      console.log("Will save the following blogPost to Firestore");
      console.log(blogPost);
    }
    saveToFirestore();
  }, [blogPost]);

  return (
    <React.Fragment>
      <div>{blogPost.title}</div>
      <div>{blogPost.body}</div>
      <div>Image 1 src: {blogPost.elements[0].src}</div>
      <div>Image 2 src: {blogPost.elements[1].src}</div>
      <div>Image 3 src: {blogPost.elements[2].src}</div>
      <button onClick={savePost}>Save Post</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

uploadImagesAndGetURL () вызывает setBlogPost () 3 раза.

А также я бы фактически получил список URL-адресов и установил elementsсвойство за один раз для предотвращения повторного рендеринга 3 раза.

...