React在setState()中提供陈旧状态

时间:2020-10-18 17:47:39

标签: javascript reactjs

在一种情况下,我有一组数据,其中文件名映射到一组图形线数据。更改复选框后, 它将从地图中删除或添加到地图中(不是JS地图-只是JS对象)。首先,添加操作可以正常工作。 删除操作似乎也起作用,并且从映射中删除文件名后,React状态似乎具有 正确更新。但是,添加其他项目时,react似乎会“复活”一个非常老的状态,该状态代表 曾经添加到地图的所有文件的集合。此行为是由特定的setState()调用引起的,我将突出显示 下面。一直在绕圈调试这一点,并陷入了死胡同。原因状态克隆在console.log()中 呼叫是我发现Chrome console.log呼叫是asynchronous。 我对深度克隆有更多的选择,但是为了消除任何不确定性,我只是深度克隆了一切。

确切的中断是什么。

  1. 选择文件1。 图上显示的1.1文件1数据
  2. 选择文件2。 图上显示的2.1文件2数据
  3. 删除文件2 3.1只有文件1数据未显示在图中
  4. 删除文件1 4.1图形上未显示任何内容。 this.state似乎在console.log输出和 反应开发工具对元素状态的检查。
  5. 选择任何文件,但说文件3 5.1图形显示文件1,文件2和文件3的数据。 5.2这是错误的-在4.1中看到状态显示未映射任何图形数据。错误介绍 通过setState()调用,该调用仅更新一个不相关的标志,该标志用于显示模式对话框。

如果我从this.setState({fetching_data: true},...中删除呼叫getFIleData(),那么一切正常。由于某些原因 如果存在该呼叫,它将收到较旧的状态。

如果有人可以阐明这一点,我将不胜感激,因为我已经耗尽了所有想法;)

class ResultsList extends React.Component
{
    state = {
        fetching_data: false,

        // Data on all available files, for which server can provide data
        //
        // [ {
        //    date: (5) [mm, dd, hh, mm, ss]
        //    pm: "pmX"
        //    fullname: "mm_dd_hh_mm_ss_pmX_TAG"
        //    tag: "TAG"
        //    serno: "1234"
        //   },
        //   ...
        // ]
        data : [],

        selected_col: "",

        // Contents of data files that have been requested from server. If item is in this
        // dictionary then is is "selected", i.e. displayed on the graph. When "deselected"
        // should be removed from this dictionary.
        //
        // { filename1 : {
        //       data: {dataset1: Array(45), dataset2: Array(45), xvalues: Array(45)}
        //       path: "blah/blah"
        //       status: 200
        //       status_str: "ok"
        //       >>>> These bits are augmented, the above is from server
        //       FAM_colour: string,
        //       HEX_color: string,
        //       <<<<
        //    },
        //    filename2 : {
        //       ...
        //    }
        // }
        file_data: {},
        file_data_size: 0,

        graph: null,
    };

    createGraphDataSetsFromFileData = (srcFileData) => {
        const newGraphDatasets = [];
        let idx_prop = 0;
        for (var prop in srcFileData) {
            if (Object.prototype.hasOwnProperty.call(srcFileData, prop)) {
                newGraphDatasets.push(
                    {
                        label: 'dataset1_' + prop,
                        fill: false,
                        lineTension: 0.5,
                        backgroundColor: 'rgba(75,192,192,1)',
                        borderColor: srcFileData[prop].FAM_colour,
                        borderWidth: 2,
                        data: srcFileData[prop].data['dataset1'],
                    }
                );
                newGraphDatasets.push(
                    {
                        label: 'dataset2_' + prop,
                        fill: false,
                        lineTension: 0.5,
                        backgroundColor: 'rgba(75,192,192,1)',
                        borderColor: srcFileData[prop].HEX_colour,
                        borderWidth: 2,
                        data: srcFileData[prop].data['dataset2'],
                    }
                );
                idx_prop = idx_prop + 1;
            }
        }
        return newGraphDatasets;
    };

    getFIleData = (filename) => {
        console.log("GETTING OPTICS");
        console.log(cloneDeep(this.state));

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// If I remove this setState call then everything works
//
        // Display the "fetching data" modal dialog
        this.setState({fetching_data: true},
            () => { 
                console.log("££££££ DIALOG SHOW COMPLETED ££££££"); console.log(cloneDeep(this.state));
            }
        );
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        fetch(`http://${this.SERVER_IP_PORT}/api/v1/get_result/${filename}`)
          .then(response => response.json())
          .then(json => {
            console.log("JSON REPLY RECEIVED")
            if (json.status !== 200) {
              alert("Failed to fetch results list")
            }
            else {
                const EXPECTED_NO_CYCLES = 45 // Oh so terribly hacky!!!
                if (json.data['dataset1'].length === EXPECTED_NO_CYCLES) {
                    this.setState( (prevState) => {
                        console.log("JSON SETSTATE PREV STATE AS"); console.log(prevState);
                        
                        // Clone the file_data map and add the new data to it
                        let newFileData = cloneDeep(prevState.file_data);
                        newFileData[filename] = cloneDeep(json); // TODO - FIXME - BAD this is a shallow copy
                        newFileData[filename].FAM_colour = COLD_COLOURS[prevState.file_data_size % COLD_COLOURS.length];
                        newFileData[filename].HEX_colour = WARM_COLOURS[prevState.file_data_size % WARM_COLOURS.length];

                        // Create new graph data from the file_data map
                        let newGraph = null;
                        if (newGraph === null) { 
                            newGraph = {
                                labels : cloneDeep(json.data['xvalues']),
                                datasets: this.createGraphDataSetsFromFileData(newFileData)
                            }
                        }
                        else {
                            newGraph = cloneDeep(prevState.graph)
                            newGraph.labels = cloneDeep(json.data['xvalues']);
                            newGraph.datasets = this.createGraphDataSetsFromFileData(newFileData)
                        }

                        const retval = {
                            file_data: newFileData,
                            file_data_size: prevState.file_data_size + 1,
                            graph : newGraph
                        };
                        console.log("------- returning:"); console.log(retval);
                        return retval;
                    }, () => {console.log("££££££ OPTICS STAT EUPDATE APPLIED ££££££"); console.log(cloneDeep(this.state)); });
                }
                else {
                    alert("Assay test run contains imcomplete data set");
                }
            }
          })
          .catch( error => {
            alert("Failed to fetch results list: " + error);
          })
          .finally( () => {
            console.log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
            this.setState({fetching_data:false},
                () => {console.log("££££££ OPTICS STAT FINALLY NOT FETCH APPLIED ££££££"); console.log(cloneDeep(this.state)); });
          });
    };

    //
    // THIS FUNCTION IS THE onChange FOR A CHECKBOX and is passed the filename of the item clicked
    handleRowSelectionChange = (fullname) => {
        if (this.state.file_data.hasOwnProperty(fullname)) { 
            console.log("CHECKBOX NOT TICKED")           
            this.setState( 
                (prevState) => {
                    // Delete the file from the map
                    let newFileData = cloneDeep(prevState.file_data);
                    delete newFileData[fullname];
                    
                    let rv = {
                        file_data: newFileData,
                        file_data_size: prevState.file_data_size - 1,
                        graph : {
                            datasets: this.createGraphDataSetsFromFileData(newFileData),
                            labels: cloneDeep(prevState.graph.labels)
                        }
                    }
                    console.log("______"); console.log(rv);
                    return rv;
                },
                () => {
                    console.log("======== DELETE UPDATE APPLIED =======");
                    console.log(cloneDeep(this.state));
                }
            );
        }
        else {
            console.log("CHECKBOX IS TICKED");
            this.getFIleData(fullname);
        }
    }

如果我选择两个文件,则我希望在图中有4个数据集,并且状态反映了这一点: enter image description here

如果我随后删除这些行,则希望看不到任何图形数据,并且该状态似乎反映了这一点: enter image description here

但是!如果我再点击第三个文件... enter image description here

引入了旧状态,具体是由

this.setState({fetching_data: true},
            () => { 
                console.log("££££££ DIALOG SHOW COMPLETED ££££££"); console.log(cloneDeep(this.state));
            }
        );
getFileData函数中的

。如果将其删除,则不会引入陈旧状态。

0 个答案:

没有答案