当前位置: 首页 > 知识库问答 >
问题:

Reactjs:使用助手类和函数从API获取数据

阎坚成
2023-03-14

我是Reactjs的初学者,并试图通过一个项目来提高自己。随着项目的进行,我现在需要设置一个结构,以便我的UI组件连接到某个RESTAPI,并使用返回的数据。这在互联网上有很多例子,很好。

我的问题是,我是否以及如何将应用编程接口连接与实际的用户界面组件分开。我相信这可能是好的,因为我将有机会在各种用户界面组件中重用应用编程接口连接功能。(在这些帮助器方法中执行与API连接相关的常见任务是理想的。)

为此,我创建了一个PrimaryForm.js文件,即UI组件。对于API调用,我创建了一个APIManager.js文件。理想情况下,APIManager不应该有任何jsx,而只有返回API调用结果到PrimaryF的函数orm.js.

我正在分享我迄今为止编写的代码,以实现这一点。

PrimaryForm.js,删除对APIManager.js的API调用(请参阅下面的HandleTestConnection部分):

import React from 'react';
import withStyles from '@material-ui/styles/withStyles';
import {Link, withRouter } from 'react-router-dom';
import Paper from '@material-ui/core/Paper';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import InputLabel from '@material-ui/core/InputLabel';
import OutlinedInput from '@material-ui/core/OutlinedInput';
import Select from '@material-ui/core/Select';
import FormHelperText from '@material-ui/core/FormHelperText';
import PrimaryFormValidator from '../../validators/PrimaryFormValidator'
import styles from '../../Styles';

import Grid from '@material-ui/core/Grid';
import Tooltip from '@material-ui/core/Tooltip';
import CancelIcon from '@material-ui/icons/Cancel';
import BackIcon from '@material-ui/icons/ArrowBackIosRounded';
import TestIcon from '@material-ui/icons/Power';
import ForwardIcon from '@material-ui/icons/ArrowForwardIosRounded';
import Button from '@material-ui/core/Button';
import APIManager from '../../managers/APIManager';

function PrimaryForm(props) {
  const { classes } = props;
  const inputLabel = React.useRef(null);
  const [labelWidth, setLabelWidth] = React.useState(0);
  React.useEffect(() => {setLabelWidth(inputLabel.current.offsetWidth);}, []);

  const [state, setState] = React.useState({
    hostname: {
      value: "test",
      isError: false,
      errorText: "",
    },
    serverIp: {
      value: "192.168.16.1",
      isError: false,
      errorText: "",
    },
    osVariant: {
      value: "Linux",
      isError: false,
      errorText: "",
    },
    databaseSid: {
      value: "mysql",
      isError: false,
      errorText: "",
    },
    listenerPort: {
      value: "3306",
      isError: false,
      errorText: "",
    },
    isFormValid: true,
    isPrimaryDbValid: false,
  });

  const evaluateFormValid = (prevState) => {
    return ((prevState.hostname.value!=="" && !prevState.hostname.isError) &&
            (prevState.serverIp.value!=="" && !prevState.serverIp.isError) &&
            (prevState.osVariant.value!=="" && !prevState.osVariant.isError) &&
            (prevState.databaseSid.value!=="" && !prevState.databaseSid.isError) &&
            (prevState.listenerPort.value!=="" && !prevState.listenerPort.isError));
  };

  const handleChange = event => {
    var valResult;
    switch (event.target.id) {
      case 'hostname':
        valResult = PrimaryFormValidator.validateHostname(event.target.value, event.target.labels[0].textContent);
        setState({
          ...state,
          hostname:
          {
            value: event.target.value,
            isError: valResult.isError,
            errorText: valResult.errorText,
          },
        });
        break;
      case 'serverIp':
        valResult = PrimaryFormValidator.validateIpAddress(event.target.value, event.target.labels[0].textContent);
        setState({
          ...state,
          serverIp:
          {
            value: event.target.value,
            isError: valResult.isError,
            errorText: valResult.errorText,
          }
        });
        break;
      case 'databaseSid':
        valResult = PrimaryFormValidator.validateDatabaseSid(event.target.value, event.target.labels[0].textContent);
        setState({
          ...state,
          databaseSid:
          {
            value: event.target.value,
            isError: valResult.isError,
            errorText: valResult.errorText,
          }
        });
        break;
      case 'listenerPort':
        valResult = PrimaryFormValidator.validateListenerPort(event.target.value, event.target.labels[0].textContent);
        setState({
          ...state,
          listenerPort:
          {
            value: event.target.value,
            isError: valResult.isError,
            errorText: valResult.errorText,
          }
        });
        break;
      default:
        //setState({...state,});
    }
    setState(prevState => ({
      ...prevState,
      isFormValid: evaluateFormValid(prevState),
    }));
  }

  const handleTestConnection = event => {
    APIManager.testConnection(state.hostname.value, state.serverIp.value, state.osVariant.value, state.databaseSid.value, state.listenerPort.value);
    //console.log("Data:" + APIManager.state.testConnectionResult);
  }

  const handleSelect = osVariant => event => {
    var valResult = PrimaryFormValidator.validateOsVariant(event.target.value, inputLabel.current.textContent);
    setState(prevState => ({
      ...prevState,
      osVariant:
      {
        value: event.target.value,
        isError: valResult.isError,
        errorText: valResult.errorText,
      },
    }));
    setState(prevState => ({
      ...prevState,
      isFormValid: evaluateFormValid(prevState),
  }));
  }

  return (
    <React.Fragment>
      <div className={classes.bigContainer}>
        <Paper className={classes.paper}>
          <div>
            <div>
              <Typography variant="subtitle1" gutterBottom className={classes.subtitle1} color='secondary'>
                Primary Database System
              </Typography>
              <Typography variant="body1" gutterBottom>
                Information related with the primary database system. Please note that the primary database has to be up and running.
              </Typography>
            </div>
            <div className={classes.bigContainer}>
              <form className={classes.formArea}>
                <TextField className={classes.formControl}
                  id="hostname"
                  label="FQDN Hostname *"
                  onChange={handleChange}
                  value={state.hostname.value}
                  error={state.hostname.isError}
                  helperText={state.hostname.errorText}
                  variant="outlined" autoComplete="off" />
                <TextField className={classes.formControl}
                  id="serverIp"
                  label="Server Ip Address *"
                  onChange={handleChange}
                  value={state.serverIp.value}
                  error={state.serverIp.isError}
                  helperText={state.serverIp.errorText}
                  variant="outlined" autoComplete="off" />
                <FormControl variant="outlined" className={classes.formControl}>
                  <InputLabel id="osVarLabel" htmlFor="osVariant" ref={inputLabel}>OS Variant *</InputLabel>
                  <Select
                    id="osVariant"
                    label="OS Variant *"
                    value={state.osVariant.value}
                    error={state.osVariant.isError}
                    onChange={handleSelect("osVariant")}
                    input={<OutlinedInput id="outlinedinput" labelWidth={labelWidth}/>}>
                    <MenuItem value={''}></MenuItem>
                    <MenuItem value={'Linux'}>Linux</MenuItem>
                    <MenuItem value={'Windows'}>Windows</MenuItem>
                  </Select>
                  <FormHelperText error={state.osVariant.isError} hidden={!state.osVariant.isError}>
                    {state.osVariant.errorText}
                  </FormHelperText>
                </FormControl>
                <TextField className={classes.formControl}
                  id="databaseSid"
                  label="Database SID"
                  onChange={handleChange}
                  value={state.databaseSid.value}
                  error={state.databaseSid.isError}
                  helperText={state.databaseSid.errorText}
                  variant="outlined" autoComplete="off" />
                <TextField className={classes.formControl}
                  id="listenerPort"
                  label="Listener Port"
                  onChange={handleChange}
                  value={state.listenerPort.value}
                  error={state.listenerPort.isError}
                  helperText={state.listenerPort.errorText}
                  variant="outlined" autoComplete="off" />
                {/* <TextField className={classes.formControl}
                  disabled={true}
                  id="isFormValid"
                  label="Is Form Valid Now?"
                  value={state.isFormValid}
                  variant="outlined" autoComplete="off" /> */}
              </form>
            </div>
          </div>      
        </Paper>
        <Grid container spacing={2} className={classes.grid}>
              <Grid item xs={12}>
                <div className={classes.flexBar}>
                  <Tooltip title="Back to previous step">
                    <div>
                      <Button variant="contained"
                        disabled={true}
                        className={classes.actionButton}
                        size='large'>
                        <BackIcon className={classes.rightIcon}/>Back
                      </Button>
                    </div>
                  </Tooltip>

                  <Tooltip title="Test Connection">
                    <div>
                      <Button variant="contained" className={classes.actionButton}
                        color="primary"
                        size='large'
                        disabled={!state.isFormValid}
                        onClick={handleTestConnection}>
                        <TestIcon className={classes.rightIcon}/>Test
                      </Button>
                    </div>
                  </Tooltip>

                  <Tooltip title="Proceed the next step">
                    <div>
                      <Button
                        variant="contained" className={classes.actionButton}
                        color="primary"
                        size='large'
                        disabled={!state.isPrimaryDbValid}>
                        <ForwardIcon className={classes.rightIcon} />Next
                      </Button>
                    </div>
                  </Tooltip>

                  <Tooltip title="Cancel creating new configuration">
                    <Button variant="contained" color="default" className={classes.actionButton}
                      component={Link} to={'/configs'} style={{ marginLeft: 'auto' }}>
                      <CancelIcon className={classes.rightIcon} />Cancel
                      </Button>
                  </Tooltip>
                </div>
              </Grid>
            </Grid>
      </div>
    </React.Fragment>
  )
}

export default withRouter(withStyles(styles)(PrimaryForm));

这是我的经理。js文件:

import React, { Component } from 'react';

export default class APIManager extends Component{
    constructor(props) {
      super(props); 
      this.state = {
        testConnectionResult: {},
        ...this.props,
      }
      this.testConnection = this.testConnection.bind(this);
    }

    static testConnection(hostname, serverIp, osVariant, databaseSid, listenerPort) {
      fetch(`http://localhost:8000/api?objtype=ConnectionDef&hostname=${hostname}&serverIp=${serverIp}&osVariant=${osVariant}&databaseSid=${databaseSid}&listenerPort=${listenerPort}`)
       .then(response => response.json())
       .then(result => this.setState({testConnectionResult: result}));
        //.catch((error) => console.error(error));
       console.log("Data:" + this.testConnectionResult);

      return this.testConnectionResult;
    }
}

现在我无法解决下面的错误:

经理。js:16未捕获(promise中)类型错误:_this2。setState不是APIManager的函数。js:16

我想我现在很难将API调用的结果传递给外部(被调用方)类/函数。

我谷歌了一下,还检查了Stackoverflow上的其他问题,但这并没有帮助我解决这个问题。我还想问我是否犯了一个主要错误。

任何帮助都将不胜感激。谢谢。

共有2个答案

袁桐
2023-03-14

您不必仅仅为了保存API调用逻辑而创建组件。in-react组件用于声明可视内容(因此它们将具有jsx)。

若要从组件中提取api调用逻辑,可以将该逻辑放入某个返回promise的函数中。像这样:

function testApi(...args) {
  return fetch(/* url parms */)
}

然后在你的组件中,在某个事件中,比如单击,你可以调用应用编程接口,并将响应数据设置为组件状态以更改视图:

handleTestEvent(...args) {
  testApi(...args)
    .then(response => response.json())
    .then(result => this.setState({testConnectionResult: result}));
    .catch((error) => {
      console.error(error));
      this.setState({testConnectionResult: []});
    });
}
盛承
2023-03-14

您不能从静态方法访问此(实例上下文)。让它成为一个常规的实例方法,它应该可以工作。

 类似资料:
  • 在5.1版本中,大部分的助手函数都可以归结为一个函数app(),因为5.1全面采用容器管理类的实例,而app()函数又是容器的“管家”。 助手函数 系统为一些常用的操作方法封装了助手函数,便于使用,包含如下: 助手函数 描述 abort 中断执行并发送HTTP状态码 action 调用控制器类的操作 app 快速获取容器中的实例 支持依赖注入 behavior 执行某个行为 bind 快速绑定对象

  • 问题内容: 几天以来,我一直在为我的实习机会创建一个React Web应用程序,但是遇到了CORS错误。我正在使用最新版本的reactJS,并将其放在中,以下是用于获取的代码: 抱歉,由于公司规定,我无法发布该API的网址,但是,可以确认该API后端中没有CORS设置。 但是,在mozilla上运行时遇到以下错误 和 如果在chrome上运行,则会出现以下错误 和 和 另一件事是,我可以在浏览器中

  • 我正在创建一个电子学习网站,那里有你可以选择的课程卡片。这是我的密码 课程ist.vue 我的问题是我无法从API链接获取数据- 我想知道我做错了什么。我也尝试使用{{list}}来获取数据,但没有成功。我现在的目标是得到所有的数据,即使它看起来很乱。抱歉我英语不好,谢谢你!

  • 我试图获取一系列api数据。根据下面的api数据,每个系列都有自己的数据,例如,一系列“bannerData”有自己的数据id,banner_title...下一个系列“home ecatData”有自己的数据,比如id,category_title...等等 在我的例子中,endpointapi是http://localhost:8000/api/homepage/ 如何在reactjs中获取这

  • 我正在创建一个fetchBill函数。分配https://randomapi.com/api/006b08a801d82d0c9824dcfdfdfa3b3c到一个api变量。它使用浏览器的fetch函数向api发出HTTP请求。它在一个函数中使用箭头函数。然后调用fetch函数,并在将其转换为JSON后返回响应。使用另一个。然后调用第一个函数,该函数将JSON数据传递给displayCartTo