React 路由器 useHistory.push 渲染组件但 useState,useEffect 不更新而不刷新

时间:2021-07-29 09:10:55

标签: javascript reactjs react-hooks react-router-dom

我正在为大学项目构建一个基本的虚拟应用程序,它是一个国际钱包应用程序。 每个用户账户可以有多个货币账户,如英镑、美元等... “/”路径用于登录,登录后应用程序重定向到“/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

任何建议将不胜感激

1 个答案:

答案 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);
            })