React Hooks/上下文和弹性 UI。函数组件中获取数据 (REST) 的问题

Posted

技术标签:

【中文标题】React Hooks/上下文和弹性 UI。函数组件中获取数据 (REST) 的问题【英文标题】:React Hooks/Context & Elastictic UI. Problem with fetched data (REST) in function Component 【发布时间】:2020-09-23 09:18:26 【问题描述】:

我对 React Hooks/Context 很陌生,所以我很感激一些帮助。请不要用你锋利的牙齿扑向我。我检查了其他解决方案和一些我以前做过的方法,但似乎无法通过“从列表中选择”方式获得它。

总结

我需要在我的 Search.js 中的 const 'allMunicipios'(对象数组)中获取 municipios 名称列表,然后显示一张卡片,其中包含来自所选 municipio 的一些数据。

任务

从 eltiempo-net REST API 获取数据。 使用 Elastic UI 中的 Combobox 异步元素从 municipios 列表中进行选择。 显示卡(也来自弹性 UI),其中包含所选市政当局的一些信息。

必须用函数组件/钩子来完成。没有课。 如有任何帮助,我将不胜感激。

我做了什么

我已经在上下文文件夹中创建了我的 reducer、上下文和类型文件,以使用这些文件来验证所有数据,然后访问组件中的数据。 我已经创建了我的 Search.js 文件。然后在 App.js 中导入 Search.js。 我已经访问了 REST API,现在在我的 Search.js 中有它

问题

不知何故,我无法遍历我获得的数据。 基本上我需要将 municipios.NOMBRE 从 api 推送到我的 search.js 组件中的数组 const allMunicipios 。但是当我控制台记录它时,它给了我未定义的信息。不知道为什么。

我将在这里分享相关的代码/组件。非常感谢花时间的人。

municipiosReducer.js

import 
  SEARCH_MUNICIPIOS,
  CLEAR_MUNICIPIOS,
  GET_MUNICIPIO,
  GET_WEATHER,
 from "./types";

export default (state, action) => 
  switch (action.type) 
    case SEARCH_MUNICIPIOS:
      return 
        ...state,
        municipios: action.payload,
      ;
    case GET_MUNICIPIO:
      return 
        ...state,
        municipio: action.payload,
      ;
    case CLEAR_MUNICIPIOS:
      return 
        ...state,
        municipios: [],
      ;
    case GET_WEATHER: 
      return 
        ...state,
        weather: action.payload,
      ;
    

    default:
      return state;
  
;

municipiosContext.js

import  createContext  from "react";

const municipiosContext = createContext();

export default municipiosContext;

MunicipiosState.js

import React,  createContext, useReducer, Component  from "react";
import axios from "axios";
import MunicipiosContext from "./municipiosContext";
import MunicipiosReducer from "./municipiosReducer";
import 
  SEARCH_MUNICIPIOS,
  CLEAR_MUNICIPIOS,
  GET_MUNICIPIO,
  GET_WEATHER,
 from "./types";

const MunicipiosState = (props) => 
  const initialState = 
    municipios: [],
    municipio: ,
  ;

  const [state, dispatch] = useReducer(MunicipiosReducer, initialState);
  //Search municipios
  //In arrow functions 'async' goes before the parameter.
  const searchMunicipios = async () => 
    const res = await axios.get(
      `https://www.el-tiempo.net/api/json/v2/provincias/08/municipios`
      // 08 means barcelona province. This should give me the list of all its municipios
    );

    dispatch(
      type: SEARCH_MUNICIPIOS,
      payload: res.data.municipios,
    );
  ;

  //Get Municipio
  const getMunicipio = async (municipio) => 
    const res = await axios.get(
      `https://www.el-tiempo.net/api/json/v2/provincias/08/municipios/$municipio.CODIGOINE`
      //CODIGOINE is in this REST API kind of the ID for each municipio.
      //I intent to use this later to get the weather conditions from each municipio.
    );

    dispatch( type: GET_MUNICIPIO, payload: res.municipio );
  ;

  const dataMunicipiosArray = [searchMunicipios];

  //Clear Municipios
  const clearMunicipios = () => 
    dispatch( type: CLEAR_MUNICIPIOS );
  ;

  return (
    <MunicipiosContext.Provider
      value=
        municipios: state.municipios,
        municipio: state.municipio,
        searchMunicipios,
        getMunicipio,
        clearMunicipios,
        dataMunicipiosArray,
      
    >
      props.children
    </MunicipiosContext.Provider>
  );
;

export default MunicipiosState;


搜索.js

import "@elastic/eui/dist/eui_theme_light.css";
import "@babel/polyfill";
import MunicipiosContext from "../contexts/municipiosContext";
import MunicipiosState from "../contexts/MunicipiosState";
import  EuiComboBox, EuiText  from "@elastic/eui";
import React,  useState, useEffect, useCallback, useContext  from "react";

const Search = () => 
  const municipiosContext = useContext(MunicipiosContext);
  const  searchMunicipios, municipios  = MunicipiosState;

  useEffect(() => 
    return municipiosContext.searchMunicipios();
  , []);

  const municipiosFromContext = municipiosContext.municipios;
  const bringOneMunicipio = municipiosContext.municipios[0];

  let municipiosNames = municipiosFromContext.map((municipio) => 
    return  label: `$municipio.NOMBRE` ;
  );

  console.log(`municipiosFromContext`, municipiosFromContext);
  console.log(`const bringOneMunicipio:`, bringOneMunicipio);
  console.log(`municipiosNames:`, municipiosNames);

  const allMunicipios = [
     label: "santcugat" ,
     label: "BARCELONETA" ,
     label: "BARCE" ,
  ];

  const [selectedOptions, setSelected] = useState([]);
  const [isLoading, setLoading] = useState(false);
  const [options, setOptions] = useState([]);
  let searchTimeout;

  const onChange = (selectedOptions) => 
    setSelected(selectedOptions);
  ;

  // combo-box
  const onSearchChange = useCallback((searchValue) => 
    setLoading(true);
    setOptions([]);

    clearTimeout(searchTimeout);

    // eslint-disable-next-line react-hooks/exhaustive-deps
    searchTimeout = setTimeout(() => 
      // Simulate a remotely-executed search.
      setLoading(false);
      setOptions(
        municipiosNames.filter((option) =>
          option.label.toLowerCase().includes(searchValue.toLowerCase())
        )
      );
    , 1200);
  , []);

  useEffect(() => 
    // Simulate initial load.
    onSearchChange("");
  , [onSearchChange]);

  return (
    <div>
      <EuiComboBox
        placeholder="Search asynchronously"
        async
        options=options
        selectedOptions=selectedOptions
        isLoading=isLoading
        onChange=onChange
        onSearchChange=onSearchChange
      />
      <button>Lista de municipios</button>
    </div>
  );
;

export default Search;

也是 主页.js

import React,  useState  from "react";
import  EuiComboBox, EuiText  from "@elastic/eui";
// import  DisplayToggles  from "../form_controls/display_toggles";
import "@babel/polyfill";
import "@elastic/eui/dist/eui_theme_light.css";
import Search from "./Search";
import MunicipioCard from "./MunicipioCard";

const Home = () => 
  return (
    <div>
      <EuiText grow=false>
        <h1>Clima en la provincia de Barcelona</h1>
        <h2>Por favor seleccione un municipio</h2>
      </EuiText>
      <Search />

      <MunicipioCard />
    </div>
  );
;

export default Home;

App.js

import "@babel/polyfill";
import "@elastic/eui/dist/eui_theme_light.css";
import  EuiText  from "@elastic/eui";
import React from "react";
import Home from "./components/Home";
import MunicipiosState from "./contexts/MunicipiosState";

import "./App.css";

function App() 
  return (
    <MunicipiosState>
      <div className="App">
        <EuiText>
          <h1>App Component h1</h1>
        </EuiText>
        <Home />
      </div>
    </MunicipiosState>
  );


export default App;


【问题讨论】:

【参考方案1】:

您正在使用 forEach 并将返回的值分配给变量,但是 forEach 不返回任何内容。你应该改用地图

  let municipiosNames = municipiosFromContext.map((municipio) => 
    return `label: $municipio.NOMBRE`;
  );

根据您的评论:

你的数据是异步加载的,所以它在第一次渲染时不可用,而且由于功能组件依赖于闭包,你的 onSearchChange 函数在创建时从闭包中获取值,即使你在其中有一个 setTimeout更新后的值不会反映

这里的解决方案是将municipiosFromContext作为依赖添加到useEffect

const onSearchChange = useCallback((searchValue) => 
    setLoading(true);
    setOptions([]);

    clearTimeout(searchTimeout);

    // eslint-disable-next-line react-hooks/exhaustive-deps
    searchTimeout = setTimeout(() => 
      // Simulate a remotely-executed search.
      setLoading(false);
      setOptions(
        municipiosNames.filter((option) =>
          option.label.toLowerCase().includes(searchValue.toLowerCase())
        )
      );
    , 1200);
  , [municipiosFromContext]);

  useEffect(() => 
    // Simulate initial load.
    onSearchChange("");
  , [onSearchChange]);

【讨论】:

你好。感谢您花时间回答。我的坏我已经将 foreach 更改为 map。我遇到的问题是onSearchChange 方法。文我尝试过滤municipiosNames.filter 它不会在搜索框中加载它。但如果我尝试使用名为allMunicipios 的硬编码数组,它就可以正常工作。所以我不明白为什么。 用您的附加查询更新了我的答案 我尝试在 useEffect 上添加 municipiosFromContext 但不起作用。再次感谢您在这件事上与我相处。很亲切 您能否在第二次调用的 onSeachChange 函数中检查是否收到了 municipiosNames 的更新值 做到了!多谢。现在我明白你的意思了。我不得不将它添加到 useCallBacK 的末尾。你真的帮了很多忙。再次感谢。兄弟

以上是关于React Hooks/上下文和弹性 UI。函数组件中获取数据 (REST) 的问题的主要内容,如果未能解决你的问题,请参考以下文章

react hooks的缺点(针对状态不同步和没有生命周期)

react hooks的缺点(针对状态不同步和没有生命周期)

React 17 - 添加 Evergreen UI 窗格会导致错误:无效的挂钩调用。 Hooks 只能在函数组件的主体内部调用

React Hooks 入门基础(详细使用)

在material-ui Tab容器中实现react hooks useState不起作用

React Hooks useEffect useState userContext userReducer应用项目