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 只能在函数组件的主体内部调用