【问题标题】:Get data from server and pass values ​to sub components in React (useEffect, useFetch..)从服务器获取数据并将值传递给 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 感谢您的解释!

标签: javascript reactjs react-hooks jwt


【解决方案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
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
  • 2020-03-22
  • 2017-11-24
  • 2018-06-30
  • 1970-01-01
相关资源
最近更新 更多