javascript 在child中更改后,列表未在React中更新

yb3bgrhw  于 2023-04-10  发布在  Java
关注(0)|答案(2)|浏览(104)

我见过很多类似的问题,但不知何故,通常的答案对我来说不起作用。
我有一个从API加载的项目的简单列表,见截图。当我按下“设置封面”按钮时,我希望项目被刷新,“设置封面”出现在另一个项目上。API清除所有cover属性,并将其设置在选定的项目上。
下面是列表的代码:

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

export default function ImageForm({reviewId}) {

    const [items, setItems] = useState([]);

    function setCover(id) {
        const newList = items.map((item) => {
            return {
                ...item,
                cover: (item.id === id),
            };
        });

        console.log('newlist', newList);
        setItems(newList);
    }

    const loadList = () => {
        fetch("/api/review/" + reviewId + "/images")
            .then((res) => res.json())
            .then((json) => {
                console.log('useEffect', json);
                setItems(json);
            });
    }

    useEffect(() => {
        loadList();
    }, []);

    return (
        <>
            <div className="row">
                <div className="col col-sm-6">
                    {items.map((data) => {
                        return <ImageItem key={data.id}
                                          data={data}
                                          reload={loadList}
                                          makeCover2={setCover}
                        />;
                    })}
                </div>
            </div>
        </>
    );

}

And this is the code for the item Component:

这是项目Component的代码:

import React, {useState, useRef, useEffect} from "react";
import axios from "axios";
import {Dropdown} from "react-bootstrap";

export default function ImageItem({data, remove, reload, makeCover2}) {
    const [imageData, setImageData] = useState(data);

    const makeCover = async function (item) {
        axios.post('/api/image/' + imageData.id + '/setcover').then(res => {
            if (res.data.errors) {
                console.error(res.data.errors);
            } else {
                makeCover2(imageData.id);
            }
        }).catch(err => {
            console.error(err);
        })
    }

    return (<div className="row my-2 py-2 image-item">

        <div className="col p-0 m-0">
            <div className="row">
                <div className="col">
                    <img className="img-fluid" alt="" src={'/upload/img/' + imageData.image_name}/>
                </div>
                <div className="col text-end">
                    <Dropdown>
                        <Dropdown.Toggle variant="success" id="dropdown-basic">Actions</Dropdown.Toggle>
                        <Dropdown.Menu>
                            <Dropdown.Item as="button" onClick={() => setShowForm(true)}>Change comment</Dropdown.Item>
                            {!imageData.cover && <Dropdown.Item as="button" onClick={makeCover}>Set cover</Dropdown.Item>}
                            <Dropdown.Item as="button" onClick={() => remove(imageData.id)}>Delete</Dropdown.Item>
                        </Dropdown.Menu>
                    </Dropdown>
                </div>
            </div>
        </div>
    </div>)

}

我尝试了两种方法:

  • 调用一个函数makeCover2,在API完成更新行和..之后更新列表中的所有项。
  • 调用一个reload函数,该函数只调用loadList函数,再次从API获取所有项。

在这两种情况下,Set cover下拉选项都停留在列表中的同一个项目上。但是在点击它之后,它应该跳转到另一个项目。这是怎么回事?

plicqrtu

plicqrtu1#

const [imageData, setImageData] = useState(data);

您已经将data属性复制到状态变量中。data中的值仅在第一次渲染时检查,以设置imageData的初始值。之后对data的更改将不会对imageData产生影响。
我看不出状态存在的理由,所以我建议您通过删除状态并直接使用prop来解决这个问题:

export default function ImageItem({ data, remove, reload, makeCover2 }) {
  const makeCover = async function (item) {
    axios
      .post("/api/image/" + data.id + "/setcover")
      .then((res) => {
        if (res.data.errors) {
          console.error(res.data.errors);
        } else {
          makeCover2(data.id);
        }
      })
      .catch((err) => {
        console.error(err);
      });
  };

  return (
    // ...
    <img className="img-fluid" alt="" src={'/upload/img/' + data.image_name}/>
    // ...
    {!data.cover && (
      <Dropdown.Item as="button" onClick={makeCover}>
        Set cover
      </Dropdown.Item>
    )}
    <Dropdown.Item as="button" onClick={() => remove(data.id)}>
      Delete
    </Dropdown.Item>
    // ...
  );
}
14ifxucb

14ifxucb2#

问题似乎出在ImageItem组件的状态更新上。你可以尝试使用一个useEffect钩子,当data属性改变时,它会更新状态:

import React, {useState, useRef, useEffect} from "react";
import axios from "axios";
import {Dropdown} from "react-bootstrap";

export default function ImageItem({data, remove, reload, makeCover2}) {
    const [imageData, setImageData] = useState(data);

    useEffect(() => {
        setImageData(data);
    }, [data]);

    const makeCover = async function (item) {
        axios.post('/api/image/' + imageData.id + '/setcover').then(res => {
            if (res.data.errors) {
                console.error(res.data.errors);
            } else {
                makeCover2(imageData.id);
            }
        }).catch(err => {
            console.error(err);
        })
    }

    return (<div className="row my-2 py-2 image-item">

        <div className="col p-0 m-0">
            <div className="row">
                <div className="col">
                    <img className="img-fluid" alt="" src={'/upload/img/' + imageData.image_name}/>
                </div>
                <div className="col text-end">
                    <Dropdown>
                        <Dropdown.Toggle variant="success" id="dropdown-basic">Actions</Dropdown.Toggle>
                        <Dropdown.Menu>
                            <Dropdown.Item as="button" onClick={() => setShowForm(true)}>Change comment</Dropdown.Item>
                            {!imageData.cover && <Dropdown.Item as="button" onClick={makeCover}>Set cover</Dropdown.Item>}
                            <Dropdown.Item as="button" onClick={() => remove(imageData.id)}>Delete</Dropdown.Item>
                        </Dropdown.Menu>
                    </Dropdown>
                </div>
            </div>
        </div>
    </div>)

}

通过这样做,imageData将始终反映从父组件传递的最新数据属性。
我希望这有帮助!

相关问题