我正在为大学项目构建一个基本的虚拟应用程序,它是一个国际钱包应用程序。 每个用户账户可以有多个货币账户,如英镑、美元等... “/”路径用于登录,登录后应用程序重定向到“/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
*** 编辑 1 ***
在 getCurrencyAccounts() 函数内部,我尝试对 auth 对象进行 console.log,最初它显示正确的值,但随后在 .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);
})
}
*** 编辑 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
*** 编辑 3 ****
这是我假设错误所在的地方,但我无法理解它是如何不起作用的。 auth 变量如何正确并且在 Request.get 中显示为 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 的结果:
Console log screenshot, can't explain why its null
任何建议将不胜感激
答案 0 :(得分:1)
每次状态改变时,整个组件都会从定义状态的地方重新渲染。在本例中为父 App 组件。
当 setAuth(auth) 被调用时,整个组件在点击 history.push('/myaccount') 之前重新渲染到 login.js 中的重定向行
这应该不是问题,但出于某种原因,在设置状态和本地存储后删除 history.push('/myaccount'),一切正常
函数handleResponse(request){ 要求 .then(响应 => {
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);
})