Gatsby Context API не отображает его значение

Пытаюсь отобразить состояние из Context API, но в консоли оно отображается как undefined и ничего не отображает.

вот файл контекста

import React, { useReducer, createContext } from "react"

export const GlobalStateContext = createContext()
export const GlobalDispatchContext = createContext()

const initialState = {
  isLoggedIn: "logged out",
}

function reducer(state, action) {
  switch (action.type) {
    case "TOGGLE_LOGIN":
      {
        return {
          ...state,
          isLoggedIn: state.isLoggedIn === false ? true : false,
        }
      }
      break

    default:
      throw new Error("bad action")
  }
}

const GlobalContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <GlobalStateContext.Provider value={state}>
      {children}
    </GlobalStateContext.Provider>
  )
}

export default GlobalContextProvider

и здесь должно отображаться значение

import React, { useContext } from "react"
import {
  GlobalStateContext,
  GlobalDispatchContext,
} from "../context/GlobalContextProvider"

const Login = () => {
  const state = useContext(GlobalStateContext)
  console.log(state)
  return (
    <>
      <GlobalStateContext.Consumer>
        {value => <p>{value}</p>}
      </GlobalStateContext.Consumer>
    </>
  )
}

export default Login

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

Есть идеи?

1 ответ

Контекстный API в целом

Из комментариев кажется, что потенциальная проблема заключается в том, что вы не рендерируете <Login /> как ребенок <GlobalContextProvider />. Когда вы используете потребителя контекста, либо как ловушку, либо как функцию, где-то в дереве компонентов должен быть соответствующий поставщик в качестве его родителя.

Например, это не сработает:

<div>
    <h1>Please log in!</h1>
    <Login />
</div>
<React.Fragment>
    <GlobalContextProvider />
    <Login />
</React.Fragment>

потому что в обоих случаях компонент входа в систему либо является родственником поставщика контекста, либо поставщик полностью отсутствует.

Однако это сработает:

<React.Fragment>
    <GlobalContextProvider>
        <Login />
    </GlobalContextProvider>
</React.Fragment>

поскольку компонент Login является дочерним по отношению к GlobalContextProvider.

Связанный с Гэтсби

Эта концепция верна независимо от того, какую библиотеку или фреймворк вы используете для создания своего приложения. В частности, в Гэтсби нужно немного поработать, чтобы заставить это работать на уровне страницы, но это возможно.

Допустим, у вас есть определенный файл Layout.jsx и следующая страница:

const Index = () => (
    <Layout>
        <h1>{something that uses context}</h1>
    </Layout>
)

У вас есть 2 варианта:

  1. Более простой вариант - извлечь это h1в собственный файл компонента. Тогда вы можете поставитьGlobalContextProvider в Layoutи поместите потребителя контекста в новый компонент. Это сработает, потому что h1 отображается как дочерний элемент макета.
  2. Стоит немного перетасовать.

Возможно, вы захотите поместить Provider в макет и попытаться использовать его на странице. Вы могли подумать, что это сработает, потому что h1 все еще отображается как дочерний элемент Layout, верно? Это правильно, но контекст не используется h1. Контекст визуализируется h1 и потребляется Index, который является родительским элементом<Layout>. Использование его на уровне страницы возможно, но вам нужно будет создать другой компонент (IndexContentили что - то подобное), потребляет ваш контекст там, и делает, что в детстве макета. Итак, в качестве примера (с исключением импорта для краткости):

const Layout = ({children}) => (
    <GlobalContextProvider>
        {children}
    </GlobalContextProvider>
);

const IndexContent = () => {
    const {text} = useContext(GlobalStateContext);
    return <h1>{text}</h1>;
}

const Index = () => (
    <Layout>
        <IndexContent />
    </Layout>
);
Другие вопросы по тегам