Three Value Logic
Three Value Logic

Reputation: 1112

Child React Component Always One Update Behind

I have a parent component and a child component.

The Parent component uploads a new image to an API. The child component displays a list of images returned from the same API.

The problem I am facing is that the child component is always one update behind. That is uploading a single image to the parent component does not refresh it but updating a second image does. Adding a third image will display the previous two images but not the third and so on.

Parent Component

function MediaLibrary(props) {
const [imageUploaded, setImageUploaded] = useState('');
const imagesGateway = ImagesGateway();

const handleUploadImage = async (event) => {
    event.preventDefault();

    let formdata = new FormData();
    formdata.append("image", event.target.files[0]);

    await imagesGateway.postArticleImages(props.articleId, formdata);

    // Update Files uploaded to try to trigger a refresh
    setImageUploaded(Math.random); 
}

return (
    <Container className="pt-4">
        <MediaList articleId={props.articleId} refresh={imageUploaded} />
        <Form.File
            id="articleImage"
            label="Upload Image"
            custom
            onChange={handleUploadImage}
        />
    </Container>
)

export default MediaLibrary;

Child Component

function MediaList(props) {
    const [images, setImages] = useState([]);
    const [loading, setLoading] = useState(true);
    const imageGateway = ImageGateway();

    useEffect(() => {
        fetchImages();
    }, [props]);

    const fetchImages = async () => {
        setLoading(true);
        const articleImages = await imageGateway.getArticleImages(props.articleId);
        setImages(articleImages);
        setLoading(false);
    }

    return (
        <Container>
            <Row>
                {images.map(image => (
                    <Image src={image} thumbnail />
                ))}
            </Row>
        </Container>
    )
}

export default MediaList;

Edit: It looks like the fetchimages function in the child component is hit every single time with the correct data from the API but this is not triggering the child component to display the fresh data.

Upvotes: 1

Views: 225

Answers (2)

Nostromo
Nostromo

Reputation: 1053

Update your child component as below to let your component properly be affected by your refresh prop changes:

function MediaList({ articleId, refresh }) {
    const [images, setImages] = useState([]);
    const [loading, setLoading] = useState(true);
    const imageGateway = ImageGateway();

    useEffect(() => {
        fetchImages();
    }, [refresh]);

    const fetchImages = async () => {
        setLoading(true);
        const articleImages = await imageGateway.getArticleImages(articleId);
        setImages(articleImages);
        setLoading(false);
    }

    return (
        <Container>
            <Row>
                {images.map(image => (
                    <Image src={image} thumbnail />
                ))}
            </Row>
        </Container>
    )
}

export default MediaList;

Upvotes: 0

Closery
Closery

Reputation: 978

Have you try to approach like this:

Parent Component

import React, { useState } from "react";
import MediaList from "./MediaList";

export default function App() {
  const [loading, setLoading] = useState(false);
  const [images, setImages] = useState([]);
  const imagesGateway = ImagesGateway();

  const handleUploadImage = async (event) => {
    event.preventDefault();

    let formdata = new FormData();
    formdata.append("image", event.target.files[0]);

    await imagesGateway.postArticleImages(props.articleId, formdata);

    // Call fetchImages() everytime when you post something new
    fetchImages();
  };

  const fetchImages = async () => {
    setLoading(true);
    const articleImages = await imageGateway.getArticleImages(props.articleId);
    setImages(articleImages);
    setLoading(false);
  };

  return (
      <Container className="pt-4">
        <MediaList images={images} />
        
        <Form.File
          id="articleImage"
          label="Upload Image"
          custom
          onChange={handleUploadImage}
        />
      </Container>
  );
}

Child Component

import React from "react";

function MediaList(props) {
  // Gettings images from props
  const { images } = props;

  return (
    <ul>
      {images.map((image) => (
        <li>
          {image.title}
          <img src={image} alt="img" />
        </li>
      ))}
    </ul>
  );
}

export default MediaList;

Upvotes: 2

Related Questions