Nextjs и Context API

Работая с Next.js, я пытаюсь сохранить данные в состоянии Context API после их извлечения в getInitialProps, чтобы исправить сверление реквизита.

Но поскольку getInitialProps - статический метод, мы не можем получить к нему доступ через this.context. Мне удалось сохранить их в componentDidMount, но в этом случае состояние контекста пусто при загрузке первой страницы, пока оно не заполнится. Не уверен, что лучше всего было бы в этом случае. В каком жизненном цикле я должен сохранять исходные данные в Context, чтобы они были сразу же, как при передаче props?

Вы решили этот вопрос?

Francis Rodrigues 18.12.2019 13:27

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

Goran Jakovljevic 18.12.2019 14:14

Я решил это с помощью ContextApi прямо сейчас. Я собираюсь опубликовать пример.

Francis Rodrigues 18.12.2019 14:38
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
19
3
23 423
2

Ответы 2

you cannot use ContextAPI in Next.js server-side (SSR), because it's against hooks rules. https://reactjs.org/warnings/invalid-hook-call-warning.html

React сначала запустит getInitialProps, поэтому лучшее решение - получить данные оттуда и передать их через ваш компонент с помощью ContextAPI.

Давайте продолжим и посмотрим, как он работает, как показано ниже:

Создайте свой компонент AppProvider

Реализуйте функции поставщика контекста, которые вы хотите передать через компоненты React.

В этом случае мы создадим наш глобальный поставщик контекста, обернув в него все приложение.

const AppProvider = ({ children }) => {
  const [galleryData, setGalleryData] = React.useState([]);

  const handleGalleryData = galleryData => {
    setGalleryData(galleryData);
  }

  const contextProps = {
    galleryData,
    handleGalleryData
  };

  return (
    <AppContext.Provider value = {contextProps}>
      {children}
    </AppContext.Provider>
  );
}

Затем оберните свое приложение этим новым провайдером.

<AppProvider>
  <App />
</AppProvider>

И на свои страницы, например index.js, попробуйте так:

Index.getInitialProps = async (props) => {
  const { req, res, query, ...others } = props;

  // use your env variables, endpoint URIs
  // ..

  ... fetch whatever you want..
  const galleryProps = await fetch(endpoint); // isomorphic-unfetch

  return {
    galleryProps,
    query,
    ...others
  };
}

В зависимости от вашей версии Next.js вы можете использовать getServerSideProps вместо getInitialProps, но не забывайте вызывать его при каждом запросе.

Next.js will pre-render this page on each request using the data returned by getServerSideProps Data Fetching docs

Начните использовать ContextAPI поверх своих компонентов

Затем в своих компонентах вы можете проверить эти данные и сохранить их в ContextAPI.

const Index = props => {
  const { galleryProps, query, ...others } = props;
  const [galleryData, setGalleryData] = useState(galleryProps);
  const { handleGalleryData, ...contextRest } = useContext(AppContext);
  ...

  // Here you're going to store data into ContextAPI appropriatly.
  useEffect(() => {
    if (typeof galleryProps === 'object' && _.keys(galleryProps).length > 0) {
      handleGalleryData(galleryProps);
    }
  }, [handleGalleryData]);

  // Other times your page is loaded, you will GET this data from ContextAPI, instead of SSR props.
  useEffect(() => {
    if (_.keys(galleryDataProps).length <= 0 && _.keys(contextRest.galleryData).length > 0) {
      setGalleryData(contextRest.galleryData);
    }
  }, []);

....

return (
  <div>
    {JSON.stringify(galleryData)}
  </div>
);

Приведенный выше вариант использования не самый лучший, но он дает понимание того, как все работает с ContextAPI в приложениях Next.js. Я объясню это ниже:

  • Первый useEffect() проверяет, получил ли компонент объект данных от реквизита, хранящего его через ContextAPI.

  • Второй проверяет, есть ли в магазине какие-то данные.

You may fetch data in SSR mode over getInitialProps before your component loads.

использованная литература

Это хороший пример, который я сейчас ищу, так что можете ли вы привести пример в codeandbox (или любой другой) ссылку с вышеупомянутой реализацией в реальном времени, которая будет полезна .. И я также сомневаюсь, следует ли реализовать redux или context api в nextjs 9 для управления состоянием. Не могли бы вы объяснить и это?

Maniraj Murugan 08.01.2020 08:33

Конечно, но я пробую что-то получше, поэтому я могу переопределить serverRuntimeConfig на своих страницах.

Francis Rodrigues 08.01.2020 14:12

Я начинаю с next.js, поэтому я ожидаю начать с хорошей структуры проекта, а также с управления состоянием .. Так что я надеюсь продолжить с примером ссылки, которую вы предоставляете, поэтому, пожалуйста, дайте мне обновленное решение с хорошим примером .. Спасибо заблаговременно..

Maniraj Murugan 08.01.2020 14:18

Я только что обновил свое решение, так что вы можете его проверить. Это так просто, чем в документации React. Попробуйте. Хорошего кода! :))

Francis Rodrigues 28.01.2020 20:01

@GMaiolo, это тот же подход, но getServerSideProps будет вызываться при каждом запросе.

Francis Rodrigues 21.05.2020 04:18

Можете ли вы предоставить codeandbox или jsfiddle рабочий пример

Vash 18.06.2020 22:12

Привет, @Vash, вот официальный пример Next.js с Context-API: github.com/vercel/next.js/tree/canary/examples/with-context-‌ api

Francis Rodrigues 04.08.2020 05:07

Когда вы говорите о getInitialProps, вы имеете в виду Server Sider Render (SSR). Если вам не нужно выполнять SSR, достаточно примера в Далее с Context API, в противном случае вы можете использовать pageProps в файле _app.js для инициализации своего контекста, подробнее о пользовательском приложении читайте в документации Пользовательское приложение

Note: If you're using Next.js 9.3 or newer, we recommend that you use getStaticProps or getServerSideProps instead of getInitialProps.

import { AppProvider } from '../contexts/AppProvider';

function MyApp({ Component, pageProps }) {
  return (
    <AppProvider initialData = {pageProps?.initialData}>
      <Component {...pageProps} />
    </AppProvider>
  );
}

export default MyApp;

Затем вы можете инициализировать свой контекст данными, полученными сервером.

import { useState, createContext, useMemo } from 'react';

export const AppContext = createContext();

export const AppProvider = ({ children, initialData }) => {
  const [data, setData] = useState(initialData);

  const value = useMemo(() => ({ data, setData }), [data]);

  return <AppContext.Provider value = {value}>{children}</AppContext.Provider>;
};

Но подождите !!, как мы можем получить данные с сервера Страницы? Это может немного сбивать с толку, но pageProps, полученные со страницы с getServerSideProps, всегда передаются через MyApp, а затем в дочерний компонент.

getServerSideProps (in Page) ===> MyApp({ Component, pageProps }) ===> Page({pageProps})

И вот как будет выглядеть Пейдж в контексте. В первый раз Сервер отображает страницу и инициализирует контекст, а затем вы можете получить данные или обновить контекст снова.


import { useContext } from 'react';
import { AppContext } from '../contexts/AppProvider';

export default function Index() {
  const { data, setData } = useContext(AppContext);

  const handleOnClick = () => {
    setData(`Data from client: ${Date.now()}`);
  };

  console.info(data);

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
      <button onClick = {handleOnClick}>Update Context</button>
    </div>
  );
}

export function getServerSideProps() {
  const data = `Data from server: ${Date.now()}`;

  return {
    props: {
      initialData: data,
    },
  };
}

Вы можете проверить, отображается ли console.info(data); на консоли сервера и клиента, но только на клиенте.

Вы можете просмотреть пример онлайн здесь

Другие вопросы по теме