我有一个组件,可以显示用户选择的经销商的联系信息。更具体地说,用户选择他们的位置,设置一个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>
</>
);
};
这是控制台日志的屏幕截图,其中记录了data
中ApiContext
返回的内容。
关于此的任何建议将不胜感激,即使这意味着完全重构我使用此方法的方式。谢谢!
答案 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
项中,以使数据在页面刷新后能够持久保存。