Material-UI-获取数据表=>“ React.useState”中的数据显示无法在回调内部调用

时间:2020-08-27 15:17:30

标签: reactjs material-ui react-hooks

我复制了ReactTables.js,并一直对其进行修改以显示自己的数据。首先,我将数据放入/variables/general.js文件中,然后将其显示在表中。

下一步是从REST服务获取数据。 我已经编写了用于执行此操作的函数,并首先在Node中甚至是fetch部分中对其进行了测试,现在我试图将它们折叠在React代码中。我知道数据针对dataTable结构的格式正确。

当我从静态数据切换到获取数据并将“ React.useState”放在.then子句下时,我开始收到错误消息:无法在回调内部调用React Hook“ React.useState”。必须在React函数组件或自定义的React Hook函数react-hooks / rules-of-hooks中调用React Hooks。 我尝试将代码放在React.use下,但看起来它想要的是Parms,而不是那里的代码。

这是我的代码,不包括动作和样式。同样,这基本上是他们的即用型代码,只是试图插入获取以获取动态数据。调用getDBLanguagesAndConvertToDataTable时,我需要使用.then。

我做了一些有关钩子的基本阅读,但是迷路了。我应该在另一个地方从REST服务中检索数据吗?

import React from "react";

// @material-ui/core components
import { makeStyles } from "@material-ui/core/styles";
// @material-ui/icons
import Assignment from "@material-ui/icons/Assignment";
import Dvr from "@material-ui/icons/Dvr";
import Favorite from "@material-ui/icons/Favorite";
import Close from "@material-ui/icons/Close";
// core components
import GridContainer from "components/Grid/GridContainer.js";
import GridItem from "components/Grid/GridItem.js";
import Button from "components/CustomButtons/Button.js";
import Card from "components/Card/Card.js";
import CardBody from "components/Card/CardBody.js";
import CardIcon from "components/Card/CardIcon.js";
import CardHeader from "components/Card/CardHeader.js";
import ReactTable from "components/ReactTable/ReactTable.js";

import { dataTable } from "variables/general.js";

import { cardTitle } from "assets/jss/material-dashboard-pro-react.js";

const styles = {
  cardIconTitle: {
    ...cardTitle,
    marginTop: "15px",
    marginBottom: "0px"
  }
};

const useStyles = makeStyles(styles);

function convertMongoLanguagesToDataTable(argDbGetResults) {
  //console.log("function convertMongoLanguagesToDataTable")
  //console.log("Input:" + JSON.stringify(argDbGetResults))
  const dataTable = {
      headerRow: ["SourceLanguage", "SourcePhrase", "TargetLanguage", "TargetPhrase", "ID"],
      footerRow: ["SourceLanguage", "SourcePhrase", "TargetLanguage", "TargetPhrase", "ID"],
      dataRows: []
      };
  var temporaryMax = argDbGetResults.length
  temporaryMax = 3  // get a smaller sample for easier debugging/testing 
  for (var j = 0; j < argDbGetResults.length; j++) {
    var row = argDbGetResults[j]
    var strRow = JSON.stringify(row);   
    //console.log(j, strRow)
    var newArray = [row.masterLanguage, row.masterPhrase, row.localizedLanguage, row.localizedPhrase, row._id]
    dataTable.dataRows.push(newArray)
  }
  //console.log("return from function convertMongoLanguagesToDataTable")
  //console.log("dataTable:" + JSON.stringify(dataTable))
  return dataTable 
}

function getDBLanguagesAndConvertToDataTable(fromLanguage, toLanguage) { 
  // pass the from/to Language on the URL 
  var url = 'http://localhost:3001/api/gettranslations/' + fromLanguage + "/" + toLanguage 
  var fetchOptions = {
          method: 'GET',
          headers: {
              'Accept': 'application/json',
          }
    }
  return fetch(url, fetchOptions)
    .then(function(data) {
      // do something with the data 
      data.json()
        .then (function(fetchResult) {
            //console.log("data.json() from Fetch From gettranslation REST service:")
            //console.log(JSON.stringify(fetchResult)) 
        
            var tmpDataTable = convertMongoLanguagesToDataTable(fetchResult)
            console.log("return from function convertMongoLanguagesToDataTable")
            console.log("PostFetch dataTable:" + JSON.stringify(tmpDataTable))

            return tmpDataTable
        }) 
    })
    .catch(function(err){
        console.log("Fetch Error url:" + url)
        console.log("Fetch Error:" + err)
        throw new Error(err) 
    })
}

export default function ReactTables() {
  
  
  var fromLanguage = 'en-US'
  var toLanguage = 'es-MX'
  getDBLanguagesAndConvertToDataTable(fromLanguage, toLanguage) 
  .then(function(dataTable) {
  console.log("tmpDataTable type=" + typeof dataTable)
  var strDataTable = JSON.stringify(dataTable, null, 3);    
  console.log("Resulting DataTable=")
  console.log(strDataTable)     
   

  if (dataTable === null || dataTable === undefined) {
    console.log('dataTable is null or undefined')
  }
  if (dataTable.dataRows === null || dataTable.dataRows === undefined) {
    console.log('dataTable.dataRows is null or undefined')
  }
  console.log("Number of dataRows=" + dataTable.dataRows.length)

  
  const [data, setData] = React.useState(

    dataTable.dataRows.map((prop, key) => {
      return {
        ID: key,
        SourceLanguage: prop[0],
        SourcePhrase: prop[1],
        TargetLanguage: prop[2],
        TargetPhrase: prop[3],
        actions: (
           // code omitted 
        )
      };
    })
  );
  const classes = useStyles();
  return (
     // code omitted 
  );
  })  // end of .then for getting dataTable 
}

编辑-第2部分

尝试Marcos回答后的新代码。我必须在地图的末尾注释掉最后的“)”。它可以编译,但是现在出现错误:

Error: Objects are not valid as a React child (found: object with keys {ID, SourceLanguage, SourcePhrase, TargetLanguage, TargetPhrase, actions}). If you meant to render a collection of children, use an array instead.
    in ReactTables (created by Context.Consumer)

代码

export default function ReactTables() {
  
  const [data, setData] = React.useState()
      
React.useEffect(() => {
  var fromLanguage = 'en-US'
  var toLanguage = 'es-MX'
  getDBLanguagesAndConvertToDataTable(fromLanguage, toLanguage) 
  .then(function(dataTable) {
        console.log("tmpDataTable type=" + typeof dataTable)
        var strDataTable = JSON.stringify(dataTable, null, 3) 
        console.log("dataTable=")
        console.log(dataTable)     
      
        if (dataTable === null || dataTable === undefined) {
          console.log('dataTable is null or undefined')
        }
        if (dataTable.dataRows === null || dataTable.dataRows === undefined) {
          console.log('dataTable.dataRows is null or undefined')
        }
        console.log("Number of dataRows=" + dataTable.dataRows.length)

        setData(dataTable)
    });
}, [])



    return dataTable.dataRows.map((prop, key) => {
      return {
        ID: key,
        SourceLanguage: prop[0],
        SourcePhrase: prop[1],
        TargetLanguage: prop[2],
        TargetPhrase: prop[3],
        actions: (
           // code omitted 
        )
      };
    })
  //);   -- had to comment this out! 
  
  const classes = useStyles();
  return (
    <GridContainer>
       // Code omitted 
    </GridContainer>
  );
}

2 个答案:

答案 0 :(得分:2)

只能在渲染函数上调用钩子,如果使用钩子,则必须始终调用钩子,并且始终以相同的顺序进行调用。

您在useState的回调中使用Promise。取而代之的是,您必须在组件上使用它,并使用useEffect来调用数据提取逻辑并根据结果设置状态。

如果需要操纵数据以显示它,则可以使用useMemo从原始数据中获取已处理的数据,并且仅在数据更改时执行此操作。确保从状态中设置的data获取数据。

最后,由于useEffect将在初始渲染后运行,因为正在检索数据或发生错误,因此数据不一定可用。您可以通过条件返回来处理此问题,只需确保将所有挂钩都放置在条件返回之前,因为如前所述,您不能在渲染之间修改对任何挂钩的任何调用。


export default function ReactTables() {
  const [data, setData] = React.useState()

  React.useEffect(() => {
    getDBLanguagesAndConvertToDataTable()
      .then(result => {
        setData(result)
      });
  }, [])

  const processedData = React.useMemo(() => data && data.dataRows.map((prop, key) => {
    return {
      ID: key,
      SourceLanguage: prop[0],
      SourcePhrase: prop[1],
      TargetLanguage: prop[2],
      TargetPhrase: prop[3],
      actions: (
         // code omitted 
      )
    };
  }), [data])
 
  const classes = useStyles()

  if (!processedData) {
    return null // Use a loading component if you prefer
  }

  return (
    <GridContainer>
       // Code omitted 
    </GridContainer>
  );
}

答案 1 :(得分:2)

Ciao,React.useState用于定义状态变量,必须在组件主体中使用。像这样:

export default function ReactTables(){
   const [data, setData] = React.useState([]) // [] this is the initial value for data
   ...
}

然后,在.then回调中,您可以通过调用setData(/*data from fetch*/)来设置此状态。

PART 2

假设dataTable.dataRows是一个数组,我认为您的问题出在map函数中,尤其是在actions中。您无法返回类似actions: (...)之类的内容,因为JSON对象中不允许使用圆括号。如果actions是一个数组,请将actions: (...)替换为actions: [...]。如果actions是对象,则将actions: (...)替换为actions: {...}