React router useHistory.push渲染组件但是useState, useEffect不刷新就不更新



我正在为大学项目构建一个基本的虚拟应用程序,这是一个国际钱包应用程序。每个用户账户可以有多个货币账户,如英镑,美元等。"/";路径是用于登录的,登录后应用会重定向到"/myaccount"路由,它被保护并呈现MyAccount组件。在MyAccount组件内部,还有另一个Switch语句,它呈现CurrencyAccounts组件,该组件提供所有CurrencyAccounts的列表,每个列表项都是指向单个帐户的链接,显示详细信息。

成功登录后,工作完美,重定向到MyAccount组件和CurrencyAccounts组件呈现完美,除了useEffect,它应该使API请求直到刷新后才更新状态。

就是说你没有任何货币账户当我刷新浏览器时,状态就会更新

代码如下:

//App.js
function App() {
const [auth, setAuth] = useState(() => JSON.parse(localStorage.getItem("auth")));
const [isAuthenticated, setIsAuthenticated] = useState(() => {
if(auth == null){
return false;
}

if(auth.isAuthenticated === false){
return false;
}
if(auth.isAuthenticated === undefined){
return false;
}
return true;
});
return (
<div className="App">
<header>
<h1>Zedland International Money transfer Wallet</h1>
</header>
<Router>
<Switch>
<Route 
exact path="/" 
render={() => 
<Login setIsAuthenticated={setIsAuthenticated}
setAuth={setAuth} auth={auth} /> 
}
/>
<Route 
path="/myaccount" 
render={() => 
<MyAccount 
setIsAuthenticated={setIsAuthenticated} 
setAuth={setAuth} 
auth={auth} /> } 
/>
</Switch>
</Router>
</div>
);
}
export default App;

//Login.js
const Login = ({setIsAuthenticated, setAuth, auth}) => {
let history = useHistory();
const [error, setError] = useState(() => "");
if(auth !== null && auth.isAuthenticated === true){
return <Redirect to="/myaccount" />
}
const inputStyle = {
display: 'block',
padding: '0.5em'
}
function handleSubmit(event) {
event.preventDefault();
const form = document.forms.login;

handleResponse(fetch("http://localhost:8080/authenticate", {
mode: 'cors',
method: 'POST',
body: new URLSearchParams({
username: form.username.value,
password: form.password.value
})
}));
}
function handleResponse(request){
request
.then(response => {
if(!response.ok || response.status !== 200){
setError("Invalid Credentials!");
}else{
return response.json();
}

})
.then((auth) => {
if(auth !== undefined){
if(auth.isAuthenticated === true){
setAuth(auth);
setIsAuthenticated(auth.isAuthenticated);
}   

localStorage.setItem("auth", JSON.stringify(auth));
history.replace('/myaccount');

}
})
.catch((error) => {
setError("Error occured: " + error);
})
}
function handleChange(event){
}
return (
<form name="login" onSubmit={handleSubmit} style={{margin: '1em 0'}}>
<fieldset style={{padding: '1em 0.5em 2em 0.5em'}}>
<h2 style={{marginBottom: '1em'}}>Log in</h2>
<input 
onChange={handleChange} 
style={inputStyle} 
type="text" 
name="username" 
required="required" 
placeholder="Enter  your username: "
/>
<input 
onChange={handleChange} 
style={inputStyle} 
type="password" 
name="password" 
required="required" 
placeholder="Enter your password: "
/>
<div style={{padding: '1em 0'}}>
<p style={{color: 'red'}}>{error}</p>
</div>
</fieldset>
<div>
<input type="submit" value="Submit"/>
</div>
</form> 
)
}
export default Login

//MyAccount.js
export default function MyAccount({setIsAuthenticated, setAuth, auth}) {
let history = useHistory();
if(auth === null || auth.isAuthenticated === false){
return <Redirect to="/" />
}
function handleLogout(event) {
setIsAuthenticated(false);
localStorage.removeItem("auth");
setAuth(null);
history.push('/');
}
return (
<main>
<h2>Hello {auth.firstName}!</h2>
<Switch>
<Route
exact
path="/myaccount"
render={() => <CurrencyAccounts auth={auth} />}
/>
<Route
exact
path="/myaccount/currency-accounts/:id"
render={() => <CurrencyAccount auth={auth} />}
/>
</Switch>
<button onClick={handleLogout}>Log Out</button>
</main>
)
}
//CurrencyAccounts.js
function CurrencyAccounts({auth}) {
const [currencyAccounts, setCurrencyAccounts] = useState([]);
const [showCreateForm, setShowCreateForm] = useState(false);


function getCurrencyAccounts(){
//This has been hard coded for convenience, change later!
Request.get(`/myaccount/${auth.id}`)
.then((accounts) => {
setCurrencyAccounts(accounts.currencyAccountSummaries);
})
.catch((error) => {
console.log("Error correctly found: " + error);
})
}

useEffect(() => {
if(currencyAccounts.length == 0){
getCurrencyAccounts();
}
},[]);
return (
<section>
{
(currencyAccounts.length == 0)
?
<h3>You dont have any currency accounts yet!</h3>
:
<dl>
<h3>My currency accounts:</h3>
{
currencyAccounts.map((currencyAccount) => {
return <li id="currencyAccountSummaryLi" 
key={currencyAccount.currencyAccountId}>
<Link 
to={`/myaccount/currency-accounts/${currencyAccount.currencyAccountId}`}>
<dt>{currencyAccount.code}</dt>
<dd>{currencyAccount.symbol + currencyAccount.balance}</dd>
</Link>
</li>
})
}
</dl>
}   
<button onClick={() => setShowCreateForm(!showCreateForm)}>
Create a new currency account
</button>
{
showCreateForm &&
<AddCurrencyAccount 
currencyAccounts={currencyAccounts}
setCurrencyAccounts={setCurrencyAccounts}
setShowCreateForm={setShowCreateForm}
auth={auth}
/>
}
</section>
)
}
export default CurrencyAccounts

*** EDIT 1

在getCurrencyAccounts()函数中,我尝试console.log auth对象,最初它显示了正确的值,但随后在.then(response)方法中,我将使用响应,它说null

function getCurrencyAccounts(){
console.log("1: ", auth);//Shows correct data
Request.get(`/myaccount/${auth.id}`)
.then((accounts) => {
console.log("2: ", auth);//Shows null
setCurrencyAccounts(accounts.currencyAccountSummaries);
})
.catch((error) => {
console.log("Error correctly found: " + error);
})
}

*** Edit 2 ****

简化CurrencyAccounts组件:

function CurrencyAccounts({auth}) {
const [currencyAccounts, setCurrencyAccounts] = useState([]);
const [showCreateForm, setShowCreateForm] = useState(false);

useEffect(() => {
if(currencyAccounts.length == 0){
Request.get(`/myaccount/${auth.id}`)
.then((accounts) => {
setCurrencyAccounts(accounts.currencyAccountSummaries);
})
.catch((error) => {
console.log("Error correctly found: " + error); //***** Here is the error, exception throwing auth is null *****
})
}
},[]);
return (
<section>
{
(currencyAccounts.length == 0)
?
<h3>You dont have any currency accounts yet!</h3>
:
<dl>
<h3>My currency accounts:</h3>
{
currencyAccounts.map((currencyAccount) => {
return <li id="currencyAccountSummaryLi" key={currencyAccount.currencyAccountId}>
<Link to={`/myaccount/currency-accounts/${currencyAccount.currencyAccountId}`}>
<dt>{currencyAccount.code}</dt>
<dd>{currencyAccount.symbol + currencyAccount.balance}</dd>
</Link>
</li>
})
}
</dl>
}   
<button onClick={() => setShowCreateForm(!showCreateForm)}>
Create a new currency account
</button>
{
showCreateForm &&
<AddCurrencyAccount 
currencyAccounts={currencyAccounts}
setCurrencyAccounts={setCurrencyAccounts}
setShowCreateForm={setShowCreateForm}
auth={auth}
/>
}
</section>
)
}
export default CurrencyAccounts

*** Edit 3 ****

这是我认为错误的地方,但它不工作超出了我。auth变量在请求中是如何正确的。Get show null

useEffect(() => {
console.log(
"From currency accounts: ", auth.id);//Works here

Request.get(`/myaccount/${auth.id}`)//Null here
.then((accounts) => {
console.log("Accounts: ", accounts);
setCurrencyAccounts(accounts.currencyAccountSummaries);
})
.catch((error) => {
console.log("Error correctly found: " + error);
})
// if(currencyAccounts.length == 0){

//}
},[]);

//Custom Request object:
get: async function(url, opts){
console.log("From Request.get: ", url);
let path = `http://localhost:8080${url}`;
let options = (opts !== undefined) ? opts : {};
let headers = (options.headers !== undefined) ? options.headers : {};

let response = await fetch(path, {
mode: 'cors',
method: 'GET',
headers: {
Authorization: this.getAuth(),
...headers
},
...options
});
try {
if(!response.ok || response.status !== 200){
throw new Error({
'error': true,
status: response.status,
...response
});
}
} catch (error) {
return error;
}
if(options.return == 'blob'){
return await response.blob();
}else if(response.return == 'text'){
return await response.text();
}else{
return await response.json();
}        
},
post: async function(url, opts){
let options = (opts !== undefined) ? opts : {};
let headers = (options.headers !== undefined) ? options.headers : {};
let body = (options.body !== undefined) ? options.body : {};
let path = `http://localhost:8080${url}`;
let response = await fetch(path, {
method: 'POST',
mode: 'cors',
headers: {
Authorization: this.getAuth(),
...options.headers
},
body: body
});
try {
if(!response.ok || response.status != 201){
throw new Error({
'error': true,
status: response.status,
...response
});
}
} catch (error) {
return error;
}
if(options.return == 'blob'){
return await response.blob();
}else if(response.return == 'text'){
return await response.text();
}else{
return await response.json();
}

},
put: async function(url, opts){
let options = (opts !== undefined) ? opts : {};
let path = `http://localhost:8080${url}`;
let body = (options.body !== undefined) ? options.body : {};
let headers = (options.headers !== undefined) ? options.headers : {};
let response = await fetch(path, {
method: 'PUT',
mode: 'cors',
headers: {
Authorization: this.getAuth(),
...options.headers
},
body: body
});
try {
if(!response.ok || response.status !== 200){
throw new Error({
'error': true,
status: response.status,
...response
});
}
} catch (error) {
return error;
}
if(options.return == 'blob'){
return await response.blob();
}else if(response.return == 'text'){
return await response.text();
}else{
return await response.json();
}

},

Console.log的结果:

控制台日志截图,无法解释为什么为空

如有任何建议,不胜感激

每次状态改变时,整个组件都会从定义状态的位置重新呈现。在本例中是父组件。

当setAuth(auth)被调用时,在到达history.push('/myaccount')

之前,整个组件会重新渲染到login.js中的重定向行。这应该不是一个真正的问题,但由于某种原因通过删除history.push('/myaccount')设置状态和本地存储后,它都工作

函数handleResponse(请求){请求不要犹豫(反应=比;{

if(!response.ok || response.status !== 200){
setError("Invalid Credentials!");
}else{
return response.json();
}

})
.then((auth) => {
if(auth !== undefined){
if(auth.isAuthenticated === true){
localStorage.setItem("auth", JSON.stringify(auth));
setAuth(auth);
}   

}
})
.catch((error) => {
setError("Error occured: " + error);
})

最新更新