直到页面刷新后,REST API响应才会更新

时间:2020-09-17 16:58:48

标签: javascript reactjs rest axios gatsby

我有一个组件,可以显示用户选择的经销商的联系信息。更具体地说,用户选择他们的位置,设置一个cookie,然后该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>
    </>
  );
};

这是控制台日志的屏幕截图,其中记录了dataApiContext返回的内容。

console.log(data)

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

2 个答案:

答案 0 :(得分: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])

答案 1 :(得分:0)

经过大量的挖掘,我自己能够弄清楚这一点。

使用Ferran的建议作为基础,我决定最好对显示状态联系信息的组件进行补水,但是由于我在多个组件中使用此上下文,因此需要更新状态全球。我从makeUseAxios转到了传统的axios通话。然后将经销商ID存储在该状态中并在呼叫中使用。我还创建了changeDealer常量,可以在上下文中传递它,并更新状态:

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项中,以使数据在页面刷新后能够持久保存。