在useEffect中使用useRef current时出现问题



我有以下问题:目前我正在尝试编写一个customHook,用它我可以进行所有的后端操作。这样做的目的是让我的代码看起来更干净、更容易理解,并减少编写。

为了理解这个问题,我必须先解释几件事。首先,我使用JWT令牌,为了执行一些操作,您需要在authentication头中使用令牌进行身份验证。如果我在尝试发布内容时遇到错误,我的函数应该刷新JWT令牌,并且如果第一次出现身份验证错误,应该再次调用该函数。因为我使用的是函数组件,而customHook(我认为(只能像函数组件那样使用钩子,所以在将状态设置为当前错误值后,我必须再次调用上一个函数。因此,我将以前的函数及其参数保存在useRefs中,但当触发useEffect时,ref.current值没有应有的值。

在以下代码中,仅包括此问题所需的操作:

使用后端:

import axios from "axios";
import { useEffect, useRef, useState } from "react";
const BASE_URL = 'https://localhost:44372/api/';
const R = 'Requests/'; const M = 'Movies/'; const U = 'Users/';

const defaultOperations = (controller) => {
return {
post: (object) => buildParams((`${controller}/Post${controller.substring(0, controller.length-1)}`), 'post', true, object),
put: (object, id) => buildParams((`${controller}/Put${controller.substring(0, controller.length-1)}/${id}`), 'put', true, object),
delete: (id) => buildParams((`${controller}/Delete${controller.substring(0, controller.length-1)}/${id}`), 'delete', true),
getAll: () => buildParams((`${controller}/Get${controller}`), 'get', false),
getByID: (id) => buildParams((`${controller}/Get${controller.substring(0, controller.length-1)}/${id}`), 'get', false),
}
}
const buildParams = (url, type, header, param) => {
return {url: url, type: type, header: header, param: param};
}

export const CONTROLLERS = {
REQUESTS: {
post: (object) => defaultOperations('Requests').post(object),
put: (object, id) => defaultOperations('Requests').put(object, id),
delete: (id) => defaultOperations('Requests').delete(id),
getAll: () => defaultOperations('Requests').getAll(),
getByID: (id) => defaultOperations('Requests').getByID(id),
getRequestsByUser: () => buildParams(`${R}GetRequestsByUser`, 'post', true, {accessToken: localStorage.accessToken}),

},
USERS: {
refreshToken: () => buildParams(`${U}RefreshToken`, 'post', false, {accessToken: localStorage.accessToken, refreshToken: localStorage.refreshToken}),
login: (mail, password) => buildParams(`${U}Login`, 'post', false, {mail: mail, password: password}),
register: (object) => buildParams(`${U}Register`, 'post', false, object),
getUserByAccessToken: () => buildParams(`${U}GetUserByAccessToken`, 'post', true, {accessToken: localStorage.accessToken}),
authentificate: () => buildParams('authentificate', 'special', true, undefined),
getAll: () => defaultOperations('Users').getAll(),
getByID: (id) => defaultOperations('Users').getByID(id),
post: (object) => defaultOperations('Users').post(object),
put: (object, id) => defaultOperations('Users').put(object, id),
delete: (id) => defaultOperations('Users').delete(id),
},
}

export const  useBackend = (error, setError, initValue) => {
const [values, setValues] = useState([]);
const lastFunction = useRef();
const lastParameter = useRef();

function selectFunction(objc) {
switch(objc.type) {
case 'get':  buildGetAndFetch(objc.url, objc.param, objc.header); break;
case 'post':  buildPostAndFetch(objc.url, objc.param, objc.header);break;
case 'put':  buildPutAndFetch(objc.url, objc.param, objc.header);break;
case 'delete':  buildDeleteAndFetch(objc.url, objc.param, objc.header);break;
case 'special': authentificate();break;
default: console.log("Error in Switch");
}
}
if(initValue!==undefined) setValues(initValue);



function buildPostAndFetch(url, param, header) {
let _param = param;
if(param?.accessToken !== undefined) {
_param = {accessToken: localStorage.accessToken};
if(param?.refreshToken) {
_param = {...param, refreshToken: localStorage.refreshToken}
}
}'GetUserByAccessToken'));
const finalurl = `${BASE_URL}${url}`;
if(header) {
axios.post(finalurl, _param, {headers: {
'Authorization': `Bearer ${(localStorage.accessToken)}`
}})
.then(res => {
response(res);
})
.catch(err => {        
authError(buildPostAndFetch, url, param, header);           
})
}
else {
axios.post(finalurl, param)
.then(res => {
response(res);
})
.catch(err => {
setError(true);
})
}
}


useEffect(() => {
}, [values])
function buildDeleteAndFetch(url, param, header) {
const finalurl = `${BASE_URL}${url}`;
if(header) {
axios.delete(finalurl, param, {headers: {'Authorization': `Bearer ${(localStorage.accessToken)}`}})
.then(res => {
setValues(values.filter(item => item.requestID !== param));
setError(false);
})
.catch(err => {
authError(buildDeleteAndFetch, url, param, header);
})
}
else {
axios.delete(finalurl, param)
.then(res => {
setValues(values.filter(item => item.requestID !== param));
setError(false);
})
.catch(err => {
setError(true);
})
}
}
function response(res) {
setValues(res.data)
setError(false);
}
function refreshToken(err) {
const finalurl= `${BASE_URL}${U}RefreshToken`;

axios.post(finalurl, {accessToken: localStorage.accessToken, refreshToken: localStorage.refreshToken})
.then(res => {
localStorage.accessToken = res.data.accessToken;
setError(err);
})
.catch(err => {
localStorage.accessToken = undefined;
localStorage.refreshToken = undefined;
setError(err);
});
}

function authError(funktion223, url, param, header) {
if(error!==true) {
lastFunction.current = funktion223;
lastParameter.current = [url, param, header];
refreshToken(true);
}
}
useEffect(() => {
if(error===true) {
if(localStorage.accessToken !== undefined && localStorage.refreshToken !== undefined) {
const lastfunc = lastFunction.current;
const lastparams = lastParameter.current;
if(lastfunc !== undefined && lastparams.length > 0 ) {
console.log(lastfunc);
console.log(lastparams[0]);
lastfunc(lastparams[0], lastparams[1], lastparams[2]);
}
}
}
}, [error])

return [values, 
(objc) => selectFunction(objc)];
}

组件说明:我是userSettings组件,当组件IdMount检查用户是否登录时,我正在调用钩子,如果没有,尝试从后端获取其他数据将毫无意义。如果他已登录,或者至少他的令牌已过期,则会刷新令牌。在UserRequests组件中,当没有错误时,将提取用户的请求。

(我不知道你是否需要这些信息,也许你认为自定义挂钩是正确的,而我只是在使用它们的组件中犯了错误(

用户设置:

import React, { useEffect, useState } from 'react';
import {Row, Col} from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import '../../App.css';
import ProfileSettings from './profileSettings';
import SettingsChooser from './settingsChooser';
// import SettingRoutings from '../settingRoutings';
import {BrowserRouter as Router, useLocation, useParams} from 'react-router-dom';
// import Routings from '../Routings.js';
import UserRequests from './userRequests';
import useAuth from '../../API/useAuthentification';
import { CONTROLLERS, useBackend } from '../../hooks/useBackend';
function UserSettings({user}) {
const {title: path} = useParams();
const [authError, setAuthError] = useState(false);
const [userValues, authentificate] = useBackend(authError, setAuthError, user);
let component;

useEffect(() => {
console.log('render');
authentificate(CONTROLLERS.USERS.getUserByAccessToken());
}, [])
useEffect(() => {
console.log(userValues);
}, [userValues])
if(path ==='logout') {
// localStorage.accessToken = undefined;
// localStorage.refreshToken = undefined
}
else if(path === 'deleteaccount') {
//TODO
//Implement  
}

component = <UserRequests user={userValues} setAuthError={setAuthError} authError={authError}/>

return (
<div classname="">
<Row className="">
<Col className="formsettings2"   md={ {span: 3, offset: 1}}>
<SettingsChooser/>
</Col>
<Col  className="ml-5 formsettings2"md={ {span: 6}}>
{authError ? <p>No Access, please Login first</p> : component}                 
</Col>
</Row>
</div>
);
}
export default UserSettings;

请求:

import React, { useEffect, useState } from 'react';
import {Table} from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import createAPIEndpoint from '../../API/callBackendAPI';
import {toast } from 'react-toastify';
import Loading from '../Alerts/loading';
import notify from '../Alerts/toasts';
import { ToastProvider } from 'react-toast-notifications';
import useAuth from '../../API/useAuthentification';
import { useHistory } from 'react-router';
import { CONTROLLERS, useBackend } from '../../hooks/useBackend';
toast.configure({

});
function UserRequests({user, authError, setAuthError}) {
const headers = ['ID', 'Title', 'Release Date', 'Producer', 'Director', 'Status', 'UTC Time', '#', '#'];
const history = useHistory();
const [requests, requestActions] = useBackend(authError, setAuthError);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
if(requests.length > 0) setLoaded(true);
}, [requests])
useEffect(() => { 
if(authError === false) requestActions(CONTROLLERS.REQUESTS.getRequestsByUser());
}, []);


const handleDelete = (e) => {
requestActions(CONTROLLERS.REQUESTS.delete(e));
}
const handleUpdate = (e) => {
console.log(e);
sessionStorage.movie = JSON.stringify(e);
history.push('/updateRequest');
}

if(loaded===false) return <Loading/>
return(
<ToastProvider>
<Table bordered  hover responsive="md">
<thead>
<tr>
{headers.map((item, index) => {
return( <th className="text-center" key={index}>{item}</th> );
})}
</tr>
</thead>
<tbody>
{requests.map((item, index) =>{
return(
<tr>
<td>{index + 1}</td>
<td>{item.movie.movieTitle}</td>
<td>{item.movie.movieReleaseDate}</td>
<td>{item.movie.movieProducer}</td>
<td>{item.movie.movieDirector}</td>
<td>{(item.requestStatus === 1 ? 'Success' : item.requestStatus ===2 ? 'Pending' : 'Denied')}</td>
<td className="col-md-3">{item.requestDate}</td>
<td><span  onClick={() => handleDelete(item.requestID)}><i  className="fas fa-times"></i></span></td>
<td><span  onClick={() => handleUpdate(item.movie)}><i  className="fa fa-pencil-alt"></i></span></td>
</tr>);
})}
</tbody>
</Table>
</ToastProvider>
);
}
export default UserRequests

useEffect运行onmount

当您将customHookuseBackenderror === true一起使用,并且在localStorage中有令牌时,它将尝试使用lastFunctionlastParameter引用,但您尚未启动它们。

useEffect(() => {
if(error===true) { //could use if(error) if error is boolean
if(localStorage.accessToken !== undefined && localStorage.refreshToken !== undefined) {
const lastfunc = lastFunction.current;
const lastparams = lastParameter.current;
if(lastfunc !== undefined && lastparams.length > 0 ) {
console.log(lastfunc);
console.log(lastparams[0]);
lastfunc(lastparams[0], lastparams[1], lastparams[2]);
}
}
}
}, [error])

如果您不希望useEffect运行onmount,您可以使用isMounted标志,如下所示:

停止在装载上运行useEffect

最新更新