数据更新后功能组件不重新渲染

Functional Component Not Re-rendering after data is updated

我正在构建一个仪表板,其中包含可编辑的 Esri 地图。组件的结构是这样的:

<Dashboard>
 <Visuals>
  <EventsMap>
   <PointsLayer/>
  </EventsMap>
 </Visuals>
</Dashboard>

当用户在 Dashboards 组件内部编辑某些内容(即地图上点的颜色)时,数据确实会传递到 PointsLayer,然后应该重新渲染并显示更新后的颜色,但仅在以下情况下更新我刷新页面。是因为我没有渲染方法吗? PointsLayer 组件:

import {useState, useEffect} from 'react';
import {loadModules} from 'esri-loader';
import {getFormattedDate} from 'Lib/helpers';
import styles from './Summary.module.css';
import point from "@arcgis/core/geometry/Point";


const PointsLayer = (props) => {
    const data = props.data;
    const color = data?.color;
    console.log(color)

    const humanize = (str) =>{
        let i, frags = str.split('_');
        for (i=0; i<frags.length; i++) {
            frags[i] = frags[i].charAt(0).toUpperCase() + frags[i].slice(1);
        }
        return frags.join(' ');
    }

    const pluralize = (val, word, plural = word + 's') => {
        const _pluralize = (num, word, plural = word + 's') =>
            [1, -1].includes(Number(num)) ? word : plural;
        if (typeof val === 'object') return (num, word) => _pluralize(num, word, val[word]);
        return _pluralize(val, word, plural);
    };


    const [graphic, setGraphic] = useState(null);
    useEffect(() => {


        loadModules(['esri/Graphic']).then(([Graphic]) => {

            // Parse out the Lat-Long from each Event and the doc_count
            for (let i = 0; i < data?.events.length; i++) {
                const point = {
                    type: "point", // autocasts as new Point
                    longitude: data?.events[i]?.location.split(",")[1],
                    latitude: data?.events[i]?.location.split(",")[0]
                };


                // Create a symbol for rendering the graphic
                const symbol = {
                    type: "simple-marker",  // autocasts as new SimpleMarkerSymbol()
                    style: "circle", color: color, // Color Selected on popup
                    size: "12px", outline: {
                        color: [255, 255, 255], // White
                        width: 1.5
                    }
                };


                // Create attributes for popup
                const attributes = {
                    watcherType: humanize(data?.events[i]?.doc_fields?.watcher_type),
                    eventCount: data?.events[i]?.doc_count,
                    plural: pluralize(data?.events[i]?.doc_count, 'Event'),
                    deviceName: data?.events[i]?.key,
                    lat: data?.events[i]?.location.split(",")[0],
                    long: data?.events[i]?.location.split(",")[1],
                    account: data?.events[i]?.doc_fields?.account_id,
                    address: data?.events[i]?.doc_fields['@service_address'],
                    meterId: data?.events[i]?.doc_fields?.meter_id,
                    lastEvent: getFormattedDate(data?.events[i]?.doc_fields['@time_raised_last'], '')
                };

                // Create popup template
                const popupTemplate = {
                    title: "{eventCount} {watcherType} {plural}",
                    content:
                        "<ul><li><b>Address:</b> {address}</li>" +
                        "<li><b>Account ID:</b> {account}</li>" +
                        "<li><b>Meter ID:</b> {meterId}</li>" +
                        "<li><b>Last Event:</b> {lastEvent}</li>" +
                        "<li><a href='https://maps.google.com/maps?q=&layer=c&cbll={lat},{long}&cbp='>Google Street View</a></li></ul>"
                };

                // Add the multiPoints to a new graphic
                const graphic = new Graphic({
                    geometry: point,
                    symbol: symbol,
                    attributes: attributes,
                    popupTemplate: popupTemplate
                });
                setGraphic(graphic);
                props.view.graphics.add(graphic);
            }
        }).catch((err) => console.error(err));

        return function cleanup() {
            props.view.graphics.remove(graphic);
        };
    }, []);

    return null;

}

export default PointsLayer

一张形象化我正在处理的图像:

尝试将 graphic 状态添加到 useEffect 依赖数组 [graphic]

useEffect(function, [graphic])useEffect(function, [props]) 但 props 可能会 re-renders 超出您的预期

一个更完整的示例如下所示。

    const [graphic, setGraphic] = useState(null);
useEffect(() => {


    loadModules(['esri/Graphic']).then(([Graphic]) => {

        // Parse out the Lat-Long from each Event and the doc_count
        for (let i = 0; i < data?.events.length; i++) {
            const point = {
                type: "point", // autocasts as new Point
                longitude: data?.events[i]?.location.split(",")[1],
                latitude: data?.events[i]?.location.split(",")[0]
            };


            // Create a symbol for rendering the graphic
            const symbol = {
                type: "simple-marker",  // autocasts as new SimpleMarkerSymbol()
                style: "circle", color: color, // Color Selected on popup
                size: "12px", outline: {
                    color: [255, 255, 255], // White
                    width: 1.5
                }
            };


            // Create attributes for popup
            const attributes = {
                watcherType: humanize(data?.events[i]?.doc_fields?.watcher_type),
                eventCount: data?.events[i]?.doc_count,
                plural: pluralize(data?.events[i]?.doc_count, 'Event'),
                deviceName: data?.events[i]?.key,
                lat: data?.events[i]?.location.split(",")[0],
                long: data?.events[i]?.location.split(",")[1],
                account: data?.events[i]?.doc_fields?.account_id,
                address: data?.events[i]?.doc_fields['@service_address'],
                meterId: data?.events[i]?.doc_fields?.meter_id,
                lastEvent: getFormattedDate(data?.events[i]?.doc_fields['@time_raised_last'], '')
            };

            // Create popup template
            const popupTemplate = {
                title: "{eventCount} {watcherType} {plural}",
                content:
                    "<ul><li><b>Address:</b> {address}</li>" +
                    "<li><b>Account ID:</b> {account}</li>" +
                    "<li><b>Meter ID:</b> {meterId}</li>" +
                    "<li><b>Last Event:</b> {lastEvent}</li>" +
                    "<li><a href='https://maps.google.com/maps?q=&layer=c&cbll={lat},{long}&cbp='>Google Street View</a></li></ul>"
            };

            // Add the multiPoints to a new graphic
            const graphic = new Graphic({
                geometry: point,
                symbol: symbol,
                attributes: attributes,
                popupTemplate: popupTemplate
            });
            setGraphic(graphic);
            props.view.graphics.add(graphic);
        }
    }).catch((err) => console.error(err));

    return function cleanup() {
        props.view.graphics.remove(graphic);
    };
}, [graphic]);

或使用道具,

    const [graphic, setGraphic] = useState(null);
useEffect(() => {


    loadModules(['esri/Graphic']).then(([Graphic]) => {

        // Parse out the Lat-Long from each Event and the doc_count
        for (let i = 0; i < data?.events.length; i++) {
            const point = {
                type: "point", // autocasts as new Point
                longitude: data?.events[i]?.location.split(",")[1],
                latitude: data?.events[i]?.location.split(",")[0]
            };


            // Create a symbol for rendering the graphic
            const symbol = {
                type: "simple-marker",  // autocasts as new SimpleMarkerSymbol()
                style: "circle", color: color, // Color Selected on popup
                size: "12px", outline: {
                    color: [255, 255, 255], // White
                    width: 1.5
                }
            };


            // Create attributes for popup
            const attributes = {
                watcherType: humanize(data?.events[i]?.doc_fields?.watcher_type),
                eventCount: data?.events[i]?.doc_count,
                plural: pluralize(data?.events[i]?.doc_count, 'Event'),
                deviceName: data?.events[i]?.key,
                lat: data?.events[i]?.location.split(",")[0],
                long: data?.events[i]?.location.split(",")[1],
                account: data?.events[i]?.doc_fields?.account_id,
                address: data?.events[i]?.doc_fields['@service_address'],
                meterId: data?.events[i]?.doc_fields?.meter_id,
                lastEvent: getFormattedDate(data?.events[i]?.doc_fields['@time_raised_last'], '')
            };

            // Create popup template
            const popupTemplate = {
                title: "{eventCount} {watcherType} {plural}",
                content:
                    "<ul><li><b>Address:</b> {address}</li>" +
                    "<li><b>Account ID:</b> {account}</li>" +
                    "<li><b>Meter ID:</b> {meterId}</li>" +
                    "<li><b>Last Event:</b> {lastEvent}</li>" +
                    "<li><a href='https://maps.google.com/maps?q=&layer=c&cbll={lat},{long}&cbp='>Google Street View</a></li></ul>"
            };

            // Add the multiPoints to a new graphic
            const graphic = new Graphic({
                geometry: point,
                symbol: symbol,
                attributes: attributes,
                popupTemplate: popupTemplate
            });
            setGraphic(graphic);
            props.view.graphics.add(graphic);
        }
    }).catch((err) => console.error(err));

    return function cleanup() {
        props.view.graphics.remove(graphic);
    };
}, [props]);

我认为只使用 graphic 状态会更接近您要查找的内容。将依赖项数组留空会导致 useEffect 挂钩仅在初始组件挂载时触发。

graphic 状态添加到数组中会告诉 useEffect 观察 graphic 状态的变化,如果发生变化,则再次触发 useEffect

这个post可能会有帮助Hooks and Dependency Arrays