【问题标题】:How to handle async api calls to backend during page refresh in react/redux?如何在 react/redux 的页面刷新期间处理对后端的异步 api 调用?
【发布时间】:2021-03-01 14:52:09
【问题描述】:

我正在设计一个前端应用程序,我需要了解行业标准或最佳实践,以便在页面刷新期间调用后端 API。

这是一个简单的设置。

  1. 三个 Navlinks Home、DisplayRepo、RedirectToRepo 由 react-router 提供服务。
  2. 主页链接是一个登陆页面。
  3. 在componentMount期间的首页,需要使用Axios,redux-thunk从后端API获取数据。

数据保存在 store 中,由 redux-persist 提供(保存在本地存储中。在页面刷新期间可用)。此数据通常在 DisplayRepo、RedirectToRepo 链接中使用,在显示在 UI 上之前,在两个页面上执行的一些规范化不同。 当用户单击 DisplayRepo 或 RedirectToRepo 链接时,数据在商店中可用。用户使用 useSelector 获取数据并显示。

到目前为止一切顺利。

我需要在以下几点上提供一些意见和说明:-

  1. 当用户刷新页面时,尽管数据已经在存储中可用,redux-persist/redux-thunk 会在内部调用后端。我的理解是,由于本地存储上已经有数据,可以调用后端来获取数据吗?
  2. 这里的最佳实践应该是什么?另一种方法是检查本地存储上的数据,如果不可用,则应使用 API 调用后端。但是如果 LS 有过时的数据怎么办?

主页:-

const Login = (props) => {
    const classes = useStyles(props);
    const dispatch = useDispatch();
    const history = useHistory();
    const [uname, setUname] = useState('');

    const { successNotific } = buttonNotification;
    const { enqueueSnackbar } = props;

    const inputRef = useRef();
    const submitBtn = useRef();

    useEffect(() => {
        inputRef.current.focus();
    }, []);

    const onChangeHandler = (e) => setUname(e.target.value);

    const handleKeyDown = (e) => {
        if (e.key === 'Enter') {
            e.target.name === 'email' && submitBtn.current.focus();
        }
    };

    return (
        <FormControl className={classes.signInForm}>
            <Header />

            <Box className={classes.inputContainer}>
                <input
                    type='email'
                    name='email'
                    ref={inputRef}
                    className='username'
                    onKeyDown={handleKeyDown}
                    placeholder='Enter GitHub UserName'
                    value={uname}
                    onChange={onChangeHandler}
                />
                <Button
                    key={successNotific}
                    disabled={uname.length === 0}
                    className={classes.btn}
                    variant='contained'
                    color='primary'
                    name='submit'
                    ref={submitBtn}
                    onClick={() =>
                        fetchRepoForNewUser(uname, enqueueSnackbar, dispatch, history)
                    }
                >
                    Submit
                </Button>
            </Box>
        </FormControl>
    );
};

当用户点击提交时被初始化的 API 调用:-

    export const fetchRepoForNewUser = async (
    userNameInUrl,
    enqueueSnackbar,
    dispatch,
    history
) => {
    const { successNotific, errorNotific, loadingNotific } = buttonNotification;
    enqueueSnackbar(loadingNotific.message, { variant: loadingNotific.variant });
    window.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__ = true;
    const isValidGitHubUser = await dispatch(getRepoAction(userNameInUrl));
    if (isValidGitHubUser) {
        enqueueSnackbar(successNotific.message, {
            variant: successNotific.variant,
        });
        history.push(`/displayuserrepo/${userNameInUrl.toLowerCase()}`);
    } else {
        enqueueSnackbar(errorNotific.message, { variant: errorNotific.variant });
    }
};

请求通过 dispatch action 到达 Redux thunk:-

`export const getRepoAction = (uname) => {
    const url = `${GITHUB_API_URL}/${uname}/repos`;
    return async (dispatch) => {
        dispatch({ type: GET_USER_REPO_LOADING });

        try {
            alert('calleddd');
            const response = await axios.get(url, axiosHeader);
            if (response?.status && response.data.length > 0) {
                dispatch({ type: GET_USER_REPO_SUCCESS, data: response.data });
                dispatch({ type: GET_USERNAME_SUCCESS, data: uname });

                return true;
            } else {
                dispatch({ type: GET_USER_REPO_FAILED });
                return false;
            }
        } catch (error) {
            dispatch({ type: GET_USER_REPO_FAILED });
            dispatch({ type: GET_USERNAME_FAILED });

            return false;
        }
    };
};
`

ListOfRepo 组件:-

const ListOfRepo = (props) => {
const { enqueueSnackbar } = props;
const history = useHistory();
const getUserNameFromRedux = (state) => state.loginReducer.uname;
const username = useSelector(getUserNameFromRedux);
const getUserRepoFromRedux = (state) => state.appReducer.data;
const rowData = useSelector(getUserRepoFromRedux);

const userNameInUrl =
    history.location.pathname !== '/' &&
    history.location.pathname.split('/')[2];

useEffect(() => {
    userNameInUrl !== username &&
        fetchRepoForNewUser(userNameInUrl, enqueueSnackbar, dispatch, history);
}, []);

const classes = useStyles(props);
const dispatch = useDispatch();

const onClickHandler = () =>
    dispatch({ type: GET_USER_REPO_SUCCESS, data: [] });

return (
    <Box className={classes.listOfRepoContainer}>
        <GridDisplay
            rowData={rowData}
            columnHeaders={columnHeaders}
            LinkComponent={LinkComponent}
        />
        <Button
            disabled={rowData.length === 0}
            className={classes.resetBtn}
            variant='contained'
            color='primary'
            onClick={onClickHandler}
        >
        Clear Repositories
        </Button>
    </Box>
);
};

【问题讨论】:

标签: reactjs redux react-redux redux-thunk redux-persist


【解决方案1】:

当用户刷新页面时,尽管数据已经在存储中可用,redux-persist/redux-thunk 会在内部调用后端。我的理解是,由于本地存储上已经有数据,可以调用后端来获取数据吗?

您的代码中没有条件检查。每当挂载ListOfRepo 时,useEffect 挂钩就会运行并调用fetchRepoForNewUser。 thunk 总是调用 GitHub API。

如果您想利用 redux 状态下的现有数据(无论它是否被持久化),那么您需要在获取之前检查该数据是否已经存在。一种简单的方法是使用 redux-toolkit 中的 createAsyncThunkcondition setting

这里的最佳做法应该是什么?另一种方法是检查本地存储上的数据,如果不可用,则应使用 API 调用后端。但是如果 LS 有过时的数据怎么办?

您正在处理 GitHub 上的更改会影响数据的情况。它在您的应用之外,因此您将无法知道数据是否已过时。

我看到了两个对我有意义的解决方案。

  1. 不要持久化数据。当他们在页面之间导航时,您会将数据保留在 redux 中,但每次访问该网站都会导致新的 fetch。

  2. 持久化数据并将时间戳与结果一起存储。在您的页面顶部,您可以有一条类似“最近同步 x 分钟/小时/天”的注释,告诉用户数据的年龄。旁边有一个“同步”按钮。当他们单击该按钮时,您将调用 API 并使用新结果更新您的持久数据。

【讨论】:

  • 感谢您的详细说明。我喜欢 createAsyncThunk 解决方案。完全有道理。此外,ListOfRepo 确实对安装进行了检查。它使用 URL 中的用户名检查登录的用户名。只有当用户改变了URL中的用户时,才会调用API,否则,会调用使用效果,但不会执行任何副作用。
  • 我完全忽略了条件检查:)
猜你喜欢
  • 2018-01-21
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 2019-01-20
  • 1970-01-01
  • 1970-01-01
  • 2019-01-20
相关资源
最近更新 更多