【问题标题】:Rest API response not updating until page is refreshed在页面刷新之前,Rest API 响应不会更新
【发布时间】:2021-01-04 15:08:53
【问题描述】:

我有一个组件可以显示用户选择的经销商的联系信息。更具体地说,用户选择他们的位置,设置一个 cookie,然后用于定义 API 调用。我使用 Axios 提取该位置的经销商的联系信息,将其存储在上下文中,然后根据需要通过几个组件显示信息:标题、“当前位置”组件等。

我目前遇到的问题是联系信息(例如,Header 中显示的信息)在用户执行页面硬刷新之前不会更新,因此,假设默认文本为该按钮类似于“查找经销商”,一旦选择了经销商,按钮标签应显示用户选择的经销商的名称。目前,它不是这样工作的。下面是Header 组件的代码,以及我的ApiContext

ApiContext.tsx

import React, { createContext } from 'react';

import axios from 'axios';
import { makeUseAxios } from 'axios-hooks';
import { useCookie } from 'hooks/use-cookie';

const contextObject = {} as any;

export const context = createContext(contextObject);

const useAxios = makeUseAxios({
  axios: axios.create({ baseURL: process.env.GATSBY_API_ENDPOINT }),
});

export const ApiContext = ({ children }: any) => {
  const [cookie] = useCookie('one-day-location', '1');
  const [{ data }] = useAxios(`${cookie}`);

  const { Provider } = context;
  return <Provider value={data}>{children}</Provider>;
};

Header.tsx

import React, { ReactNode, useContext, useEffect, useState } from 'react';

import Logo from 'assets/svg/logo.svg';
import css from 'classnames';
import { Button } from 'components/button/Button';
import { Link } from 'components/link/Link';
import { MenuIcon } from 'components/menu-icon/MenuIcon';
import { context } from 'contexts/ApiContext';

import { NotificationBar } from '../notification-bar/NotificationBar';
import s from './Header.scss';
import { MainNav } from './navigation/MainNav';

interface HeaderProps {
  navigationContent: ReactNode;
}

export const Header = ({ navigationContent }: HeaderProps) => {
  const [scrolled, setScrolled] = useState(false);
  const [open, setOpen] = useState(false);

  const data = useContext(context);
  
  const buttonLabel = data ? data.name : 'Find a Dealer';
  const buttonLink = data ? `tel:${data.phone}` : '/find-a-dealer';

  useEffect(() => {
    const handleScroll = () => {
      const isScrolled = window.scrollY > 10;
      if (isScrolled !== scrolled) {
        setScrolled(!scrolled);
      }
    };

    document.addEventListener('scroll', handleScroll, { passive: true });

    return () => {
      document.removeEventListener('scroll', handleScroll);
    };
  }, [scrolled]);

  return (
    <>
      <NotificationBar notificationContent={navigationContent} />
      <header className={scrolled ? css(s.header, s.header__scrolled) : s.header}>
        <nav className={s.header__navigation}>
          <ul className={s.header__container}>
            <li className={s.header__logo}>
              <Link to="/" className={s.header__link}>
                <Logo />
              </Link>
            </li>

            <li className={s.header__primary}>
              <MainNav navigationItems={navigationContent} />
            </li>

            <li className={s.header__utility}>
              <Button href={buttonLink}>{buttonLabel}</Button>
            </li>

            <li className={s.header__icon}>
              <MenuIcon onClick={() => setOpen(!open)} />
            </li>
          </ul>
        </nav>
      </header>
    </>
  );
};

这是我的控制台日志的屏幕截图,我在其中记录了从 data 返回的内容到 ApiContext

对此的任何建议将不胜感激,即使这意味着完全重构我使用它的方式。谢谢!

【问题讨论】:

    标签: javascript reactjs rest axios gatsby


    【解决方案1】:

    您快到了,您的ApiContext 看起来不错,它会检索信息并填充上下文,但是,您缺少的是一个useState 来触发更新以强制重新水合您的按钮。

    发生的情况是您的上下文永远不会更新 data 常量。在第一次渲染是空的,一旦你的请求完成并且context 已满,但你的按钮永远不会被更新。这样的事情可能对你有用:

      const data = useContext(context);
      const [newData, setNewData] = useState(data);
        
      const buttonLabel = newData? newData.name : 'Find a Dealer';
      const buttonLink = newData? `tel:${newData.phone}` : '/find-a-dealer';
    

    您可能需要稍微调整代码以满足您的要求,但是,您可以保留这个想法,即使用检索到的数据创建一个状态。

    您可以创建useEffect 来控制数据何时更改并根据需要填充状态:

    useEffect(()=>{
      setNewData(data)
    }, [data])
    

    【讨论】:

    • 谢谢,这是一个很好的开始!将它与useEffect 集成在一起,我仍然只能看到按钮在页面刷新时更新。还有什么地方可以引导我?
    • 您能否通过放置console.logs/debugger 来检查您的代码是如何重新水合的?
    • 具体是哪一部分?
    • 我想看看data是否正确填写
    • 问题已更新为截图!谢谢!
    【解决方案2】:

    经过大量挖掘,我能够自己解决这个问题。

    以 Ferran 的建议为基础,我决定最好对显示某个状态的联系信息的组件进行再水化,但由于我在多个组件中使用此上下文,因此我需要更新状态全球范围内。我离开了makeUseAxios,转而使用传统的axios 电话。然后将经销商 ID 存储在状态中并在呼叫中使用。我还创建了changeDealer const,我可以通过上下文传递它并更新状态:

    ApiContext.tsx

    import React, { createContext, useEffect, useState } from 'react';
    
    import axios from 'axios';
    
    const contextObject = {} as any;
    
    export const context = createContext(contextObject);
    
    export const ApiContext = ({ children }: any) => {
      const [dealerId, setDealerId] = useState(`1`);
    
      useEffect(() => {
        axios.get(`${process.env.GATSBY_API_ENDPOINT}/${dealerId}`).then((res) => setDealerId(res.data));
      }, [dealerId]);
    
      const changeDealer = (value: any) => {
        setDealerId(value);
      };
    
      const { Provider } = context;
      return <Provider value={{ data: dealerId, changeDealer: changeDealer }}>{children}</Provider>;
    };
    

    然后,例如,如果我有一个更新经销商信息的按钮,我将上下文导入组件并通过它传递changeDealer

    import { context } from 'contexts/ApiContext';
    const { changeDealer } = useContext(context);
    

    然后我可以像这样将它附加到一个按钮上:

    <Link to="/" onClick={() => changeDealer(dealer.id)}>
      Set Location
    </Link>
    

    这会全局更新状态,更改显示它的所有组件的联系信息。我会将数据存储在 localStorage 项目中,允许数据在页面刷新后保留。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-05
      • 1970-01-01
      • 1970-01-01
      • 2014-10-03
      • 1970-01-01
      • 2020-08-19
      相关资源
      最近更新 更多