【问题标题】:Best practice of setting state from axios request从 axios 请求设置状态的最佳实践
【发布时间】:2021-12-31 11:42:27
【问题描述】:

我有一个具有以下功能的 api.js 文件:

export const login =  (loginUserName, loginPassword, setUser) => {
    axios({
        method: 'post',
        data: {
            username: loginUserName,
            password: loginPassword
        },
        withCredentials: true,
        url: '...........'
    }).then(async (res) => {
        if (res.data.loggedIn === true) setUser(loginUserName);
    });
};

我的 useState 挂钩如下所示:

const [ user, setUser ] = useState({});

我想知道,而不是像我一样从我的登录组件发送 setUser 函数:

login(userName,password,setUser);

它在另一个文件中,我如何从函数外部执行它,如下所示:

setUser(login(userName,password));

因此,如果该函数确实检索到了我想要的信息,它将返回它(在本例中为用户名),如果不是,它会将用户设置为未定义之类的。

我尝试过使用

export const login =  (loginUserName, loginPassword) => {
   axios({
       method: 'post',
       data: {
           username: loginUserName,
           password: loginPassword
       },
       withCredentials: true,
       url: '............'
   }).then(async (res) => {
       if (res.data.loggedIn === true) return loginUserName;
   });
};

但我在使用异步功能时遇到了一些困难。 似乎由于某种原因它不会返回该信息。 如果我不得不猜测它只为 axios 函数或其他东西返回它.. 如何获得我正在寻找的效果,最佳做法是什么?发送“setUser”函​​数是否被认为是一种好习惯,还是尝试摆脱它更好?

【问题讨论】:

    标签: javascript reactjs ajax asynchronous axios


    【解决方案1】:

    所以这里发生了一些不同的(但相关的、有效的)事情。首先,这个

    setUser(login(userName,password));
    

    不会工作。您无法同步与服务器对话。您当前从useState 传递setter 作为回调的方法是fine。真的。这样做绝对没有错。

    但我也不会称其为最佳实践。 “最佳实践”有点主观,但您可以通过以下方式做到这一点:

    // useUser.js
    import React, { createContext, useContext, useState, useEffect } from 'react'
    
    // could be in another file and imported, whatever.
    const login = async (userName, userPass) => {
      const data = await axios(...);
      return data.loggedIn ? data : null; // could also throw
    };
    
    const UserContext = createContext([]); // user data and setter, eventually
    const UserProvider = ({ children }) => {
      const userState = useState({});
      return (<UserContext.Provider value={userState}>
        {children}
      </UserContext.Provider>);
    };
    
    export const useUser = () => {
      const [userData] = useContext(UserContext);
      return userData
    }
    
    export useLogin = (userName = '', userPass = '') => {
      const [_, setUserData] = useContext(UserContext);
      useEffect(() => {
        if (userName && userPass) {
          login(userName, userPass)
            .then(data => {
              if (data) setUserData(data);
            }); // add .catch to handle errors
        }
      }, [userName, userPass, setUserData])
    }
    

    然后,在你的App.js

    import React from 'react';
    import { UserProvider } from 'path/to/file.js';
    function App({ children }) {
      // code
      return <UserProvider>{children}</UserProvider>;
    }
    

    在任何依赖用户数据的组件中,您现在都可以

    import React from 'react';
    import { useUser } from 'path/to/file.js';
    
    export default (props) => {
      const userData = useUser();
      return <div>{userData.name || 'loading'}</div>;
    }
    

    在您的登录表单中,您可以这样做

    import React, { useState } from 'react';
    import { useLogin } from 'path/to/file.js';
    
    export default (props) => {
      const [userName, setUserName] = useState('');
      const [userPass, setUserPass] = useState('');
      useLogin(userName, userPass);
      return <form>...</form>;
    };
    

    通过创建上下文和自定义钩子,现在树中的任何组件都可以通过调用钩子来访问用户数据。您不想为此而疯狂,但是用户数据通常用于各个级别,并且将其作为道具传递给所有中间组件是很乏味的。这种模式can be abstracted,虽然它可能看起来像很多代码,但它比所有 redux 更容易增加页面重量。

    因为我们不导出上下文对象本身或用户数据设置器,所以您不必担心消费者会意外修改他们不应该修改的内容,这里我们只公开功能类似于带有设置器的 API 的钩子(useLogin) 和一个 getter (useUser)。

    您还可以将上下文提供程序移到树的下方(或者再往上移动,您可以将您的 App 组件包装在其中),但这取决于您需要在哪里使用它。

    【讨论】:

      【解决方案2】:

      您的 setUser 函数仅在声明它的组件内有效,所以是的,只需返回获取结果并处理在该特定组件内设置状态。

      你没有得到任何返回的原因可能是请求没有成功(检查你的浏览器的网络选项卡和相关的响应)或者你没有await结果(获取总是一个异步操作,因此您必须期待响应延迟)。

      你可以这样做:

      useEffect(() => {
        (async () => {
          const username = await login(userName,password,setUser);
          setUser(username);
        })();
      }, []);
      

      【讨论】:

        【解决方案3】:

        如果我需要在我的应用流程中使用的状态对象,例如我计划传递给大多数子组件的用户对象。那么我宁愿使用 Redux 库来创建一个我可以在所有组件中访问的商店。

        要让它发挥作用还有很多工作要做,所以我在阅读Documentation 时会小心,但使用给定的工具是值得的。

        然后您可以访问/操作存储状态,如下所示:

        import React, { useState } from 'react';
        import { useAppSelector, useAppDispatch } from 'app/hooks';
        import { decrement, increment } from './counterSlice';
        
        function login(loginUserName, loginPassword) {
            const dispatch = useAppDispatch();
        
            axios({
                method: 'post',
                data: {
                    username: loginUserName,
                    password: loginPassword
                },
                withCredentials: true,
                url: '............'
            }).then(async (res) => {
                if (res.data.loggedIn === true) {
                    dispatch(setUser(loginUserName));
                }
            };
        }
        
        export function Login() {
            login("User Name", "********");
        
            const user = useAppSelector((state) => state.user.value);
            console.log(user);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-05-07
          • 2013-08-22
          • 2020-11-20
          • 2021-06-02
          • 2021-03-26
          • 1970-01-01
          • 2014-02-20
          相关资源
          最近更新 更多