Сохранение ответа Axios в AppContext

#javascript #reactjs #axios #react-hooks #use-context

#javascript #reactjs #axios #реагирующие крючки #использование-context

Вопрос:

Я пытаюсь создать раздел моего сайта, который будет динамически извлекать контактную информацию из REST API. На данный момент я использую образец API отhttps://jsonplaceholder.typicode.com.

Я пытаюсь использовать axios , в частности, хуки useAxios и makeUseAxios , для извлечения API и сохранения его в контексте приложения, который затем я могу использовать по всему сайту, предпочтительно с useContext хуком. Кроме того, мне нужно иметь возможность обновлять вызов API на основе пользовательских взаимодействий, позволяя им выбирать местоположение, обновлять API и сохранять это в контексте приложения, чтобы все это обновлялось динамически.

По сути, я получил базовый useContext сценарий, работающий на основе этого руководства, но я затрудняюсь с тем, как сохранить ответ JSON таким образом, чтобы я мог ссылаться на него useContext . Вот самое близкое, что у меня было до сих пор:

AppContext.tsx

 import React, { createContext, ReactNode } from 'react';

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

export const AppContext = createContext();

export const DealerContextProvider = ({children}: any) => {
  const useAxios = makeUseAxios({
    axios: axios.create({ baseURL: 'https://jsonplaceholder.typicode.com/users/' }),
  });

  const LOCAL_STORAGE_KEY_DEALER = '_selectedDealerInformation';

  const [cookie] = useCookie('one-day-location', '1');
  const [dealerInfo] = useAxios(`${cookie}`);

  return (
    <AppContext.Provider value={[dealerInfo]}>
      {children}
    </AppContext.Provider>
  );
};
  

И мой компонент заголовка, где я пытаюсь получить к нему доступ:

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

import { AppContext } from 'components/app-context/AppContext';

import Logo from 'assets/svg/logo.svg';
import css from 'classnames';
import { Button } from 'components/button/Button';
import { Link } from 'components/link/Link';

import { NotificationBar } from '../notification-bar/NotificationBar';
import s from './Header.scss';
import { MenuIcon } from 'components/menu-icon/MenuIcon';
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 blogInfo = useContext(AppContext);
  const buttonLabel = blogInfo ? `${blogInfo.name}` : 'Find a Dealer';
  const buttonLink = blogInfo ? `tel:${blogInfo.name}` : '/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}
        data-open={open ? 'true' : ''}
      >
        <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__burger}>
              <MenuIcon onClick={() => setOpen(!open)} />
            </li>
          </ul>
        </nav>
      </header>
    </>
  );
};
  

Что мне нужно, так это чтобы кнопка в header__utility динамически отображала имя и номер телефона выбранного дилера. Я могу уточнить все, что необходимо, я новичок в React и все еще учусь выражать все, что мне нужно.

Спасибо!

Ответ №1:

Хорошо, я провел тонну раскопок за последние 24 часа, и я смог найти решение.

У меня есть свой контекст, теперь названный 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>;
};
  

Затем, чтобы использовать его во всех компонентах, я оборачиваю свой AppLayout в <ApiContext> :

AppLayout.tsx

 import React, { ReactNode } from 'react';

import { ApiContext } from 'contexts/ApiContext';
import { graphql, StaticQuery } from 'gatsby';

import { Devtools } from '../devtools/Devtools';
import { Footer } from '../footer/Footer';
import { Header } from '../header/Header';
import s from './AppLayout.scss';

interface AppLayoutProps {
  children: ReactNode;
  location: string;
}

const isDev = process.env.NODE_ENV === 'development';

// tslint:disable no-default-export
export default ({ children }: AppLayoutProps) => {
  return (
    <StaticQuery
      query={`${NavQuery}`}
      render={(data) => (
        <>
          <ApiContext>
            <Header navigationContent={data.prismic.allNavigations.edges[0].node} />

            <div className={s.layout}>
              {children}

              <Footer navigationItems={data.prismic.allNavigations.edges[0].node} />

              {isDev amp;amp; <Devtools />}
            </div>
          </ApiContext>
        </>
      )}
    />
  );
};

const NavQuery = graphql`
  query NavQuery {
    prismic {
      allNavigations {
        edges {
          node {
            ...NotificationBar
            ...NavigationItems
            ...FooterNavigationItems
          }
        }
      }
    }
  }
`;
  

И в моем Header компоненте я могу получить доступ к данным с помощью useContext перехвата:

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);

  console.log(data);

  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}
        data-open={open ? 'true' : ''}
      >
        <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__burger}>
              <MenuIcon onClick={() => setOpen(!open)} />
            </li>
          </ul>
        </nav>
      </header>
    </>
  );
};
  

Я все еще работаю над дополнительной оптимизацией, в частности, устраняя мерцание при загрузке страницы, а также обновляя API при нажатии кнопки, используя useCookie , который является пользовательским подключением, которое я создал. Надеюсь, это даст некоторую ясность всем, кто ищет эту информацию, мне потребовались часы, чтобы определить решение. Приветствия.