从服务器获取数据并将值传递给 React 中的子组件(useEffect、useFetch..)
Posted
技术标签:
【中文标题】从服务器获取数据并将值传递给 React 中的子组件(useEffect、useFetch..)【英文标题】:Get data from server and pass values to sub components in React (useEffect, useFetch..) 【发布时间】:2022-01-05 16:11:59 【问题描述】:为了从服务器接收一个值并将其传递给子组件,使用了useLayoutEffect。顺便说一句,useLayoutEffect 是在屏幕全部绘制完成后执行的。 由于执行顺序,传递值很困难。
第一季度。我想在渲染之前运行 useLayoutEffect ,或者知道另一种方式。 有什么方法可以推荐吗?
第二季度。在ApiServiece.js
代码的底部,有json 作为响应的返回值。但是当我输出 json 时,会输出promise[pending]
。怎么了?
我正在自学,所以我迫切需要帮助。欢迎任何cmets,所以请帮忙.????
代码结构
// AdminContainer.js
import React, useLayoutEffect, useEffect, useState, useRef from "react";
import useResolvedPath from "react-router";
import getAllUserList from "../service/ApiService";
import DisplayUser from "./DisplayUser";
const AdminContainer = () =>
const [allUsers, setUsers] = useState([]);
useLayoutEffect (() =>
setUsers(getAllUserList);
// console.log(getAllUserList()); // Promise <pending>
, []);
return (
<div>
/* JSON.stringify(allUserList[0]) */
/* allUserList.map((user, i) =>
return <DisplayUser inputUser=user key=i />
) */
<DisplayUser inputUser=allUsers />
</div>
)
export default AdminContainer;
// ApiServiece.js
import connect from 'react-redux';
import login from '../modules/authentication';
import store from '../modules/store';
import API_BASE_URL from "./app-config";
import resolvePath from 'react-router';
import Sync from '@material-ui/icons';
const ACCESS_TOKEN = "ACCESS_TOKEN";
export function signin(userDTO)
console.log(store.getState());
return call("/auth/signin", "POST", userDTO).then((response) =>
if (response.token)
localStorage.setItem(ACCESS_TOKEN, response.token);
localStorage.setItem("SequenceEmail", response.email);
window.location.href = "/";
);
export function signup(userDTO)
return call("/auth/signup", "POST", userDTO);
export function call(api, method, request)
let headers = new Headers(
"Content-Type": "application/json",
);
const accessToken = localStorage.getItem("ACCESS_TOKEN");
if (accessToken && accessToken !== null)
headers.append("Authorization", "Bearer " + accessToken);
let options =
headers: headers,
url: API_BASE_URL + api,
method: method,
;
if (request)
options.body = JSON.stringify(request);
return fetch(options.url, options)
.then((response) =>
response.json().then((json) =>
if (!response.ok)
return Promise.reject(json);
return json;
)
)
.catch((error) =>
console.log(error.status);
if (error.status === 403)
window.location.href = "/login";
return Promise.reject(error);
);
export function getAllUserList()
let result = call("/auth/getAllUerList", "GET");
return result;
// DisplayUser.js
const DisplayUser = (inputUser) =>
const userRef = useRef(inputUser);
const [user, setUser] = useState();
useEffect(() =>
setUser(userRef.current.inputUser);
, [])
console.log('setUser isArray:' + Array.isArray(setUser(userRef.current.inputUser)));
return (
// <div> user.username </div>
<div> test </div>
)
export default DisplayUser;
CodeSandbox 执行顺序Link
【问题讨论】:
在 useLayoutEffect 中使用异步函数是没有意义的,因为 useLayoutEffect 应该在重绘之前对 DOM 进行同步突变。我看到的第二个问题是 setUsers(fn(): Promise) => 它不返回数组,而是一个 Promise。 @bigless 感谢您的解释! 【参考方案1】:首先你要为allUsers
变量(应该是一个列表)分配一个没有意义的函数。您必须首先调用该函数(并使用 useLayoutEffect 在渲染之前调用它,但请求将异步解析,因此您必须仅在请求被解析时填充allUsers
)。一种解决方案是同时显示一个微调器,例如:
// AdminContainer.js
import React, useLayoutEffect, useEffect, useState, useRef from "react";
import useResolvedPath from "react-router";
import getAllUserList from "../service/ApiService";
import DisplayUser from "./DisplayUser";
const AdminContainer = () =>
const [allUsers, setUsers] = useState([]);
const [showSpinner, setSpinnerIsShowing] = useState(false)
useLayoutEffect (() =>
setSpinnerIsShowing(true)
getAllUserList().
then(users => setUsers(users))
.catch(err => /*Handle error*/)
.finally(()=> setSpinnerIsShowing(false))
, []);
return (
showSpinner ? <SomeSpinner /> : <div>
/* JSON.stringify(allUsers[0]) */
/* allUsers.map((user, i) =>
return <DisplayUser inputUser=user key=i />
) */
<DisplayUser inputUser=allUsers />
</div>
)
export default AdminContainer;
然后在 ApiService.js 中,我将使用异步等待语法来使所有内容更具可读性。
区别在于async
函数总是会返回一个promise,所以你不需要直接使用Promise类(throw
会等价于reject
,而return
会等价于resolve
)。
export async function call(api, method, request)
let headers = new Headers(
"Content-Type": "application/json",
);
const accessToken = localStorage.getItem("ACCESS_TOKEN");
if (accessToken && accessToken !== null)
headers.append("Authorization", "Bearer " + accessToken);
let options =
headers: headers,
url: API_BASE_URL + api,
method: method,
;
if (request)
options.body = JSON.stringify(request);
const response = await fetch(options.url, options)
const json = await response.json()
if (!response.ok)
const error = new CustomError("Request not OK :C");
error.customData = json;
throw error
return json;
//console.log(err.status);
//if (err.status === 403)
// window.location.href = "/login"; <-- Nope: handle this when calling the function
//
注意Custom Error
,您可以创建它来定义扩展默认javascript Error
类的附加数据:
class CustomError extends Error
constructor(message, customData = )
super(message)
this.customData = customData
getUserList 函数也是一个异步函数:
您可以通过两种方式创建异步函数:
使用异步语法
export async function getAllUserList()
return await call("/auth/getAllUserList", "GET");
承诺语法:
export function getAllUserList()
return new Promise((reject,resolve)=>
call("/auth/getAllUserList", "GET")
.then(res => resolve(res))
.catch(err => reject(err))
)
这两者是等价的。
如果您需要澄清,请在 cmets 中告诉我 :)
【讨论】:
非常感谢! @Antonio Della Fortuna以上是关于从服务器获取数据并将值传递给 React 中的子组件(useEffect、useFetch..)的主要内容,如果未能解决你的问题,请参考以下文章
使用 React Hooks 获取帖子并将它们作为附加道具传递给另一个组件
如何使用从 React 中的子组件获取的数据更新父组件中的状态
如何将函数从父级传递给深层嵌套的子级并将@input 值用于Angular 8中传递的函数?