Реагировать контекст не обновляется - PullRequest
2 голосов
/ 16 марта 2020

Я установил базовый c пример проекта, который использует Context для хранения заголовка страницы, но когда я его установил, компонент не перерисовывается.

Основные файлы:

Context. js

import React from 'react'

const Context = React.createContext({})

export default Context

AppWrapper. js

import React from 'react'
import App from './App'
import Context from './Context'

function AppWrapper () {
  return (
    <Context.Provider value={{page: {}}}>
      <App />
    </Context.Provider>
  )
}

export default AppWrapper

Приложение. js

import React, { useContext } from 'react';
import Context from './Context';
import Home from './Home';

function App() {
  const { page } = useContext(Context)
  return (
    <>
      <h1>Title: {page.title}</h1>
      <Home />
    </>
  );
}

export default App;

Home. js

import React, { useContext } from 'react'
import Context from './Context'

function Home () {
  const { page } = useContext(Context)
  page.title = 'Home'

  return (
    <p>Hello, World!</p>
  )
}

export default Home

полный код

Что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 16 марта 2020

Думайте о контексте React так же, как о компоненте, если вы хотите обновить значение и показать его, вам нужно использовать state. В этом случае вы AppWrapper, где вы визуализируете, провайдер контекста находится там, где вам нужно отслеживать состояние.

import React, {useContext, useState, useCallback, useEffect} from 'react'

const PageContext = React.createContext({})

function Home() {
  const {setPageContext, page} = useContext(PageContext)
  // essentially a componentDidMount
  useEffect(() => {
    if (page.title !== 'Home')
      setPageContext({title: 'Home'})
  }, [setPageContext])
  return <p>Hello, World!</p>
}

function App() {
  const {page} = useContext(PageContext)
  return (
    <>
      <h1>Title: {page.title}</h1>
      <Home />
    </>
  )
}

function AppWrapper() {
  const [state, setState] = useState({page: {}})
  const setPageContext = useCallback(
    newState => {
      setState({page: {...state.page, ...newState}})
    },
    [state, setState],
  )
  const getContextValue = useCallback(
    () => ({setPageContext, ...state}),
    [state, updateState],
  )
  return (
    <PageContext.Provider value={getContextValue()}>
      <App />
    </PageContext.Provider>
  )
}

Редактировать - Обновленное рабочее решение из связанного хранилища

Я переименовал несколько что-то более конкретное c, я бы не рекомендовал передавать setState через контекст, поскольку это может привести к путанице и конфликту с локальным состоянием в компоненте. Также я опускаю куски кода, которые не нужны для ответа, только части, которые я изменил

src / AppContext. js

export const updatePageContext = (values = {}) => ({ page: values })
export const updateProductsContext = (values = {}) => ({ products: values })

export const Pages = {
  help: 'Help',
  home: 'Home',
  productsList: 'Products list',
  shoppingCart: 'Cart',
}

const AppContext = React.createContext({})

export default AppContext

src / AppWrapper . js

const getDefaultState = () => {
  // TODO rehydrate from persistent storage (localStorage.getItem(myLastSavedStateKey)) ?
  return {
    page: { title: 'Home' },
    products: {},
  }
}

function AppWrapper() {
  const [state, setState] = useState(getDefaultState())

  // here we only re-create setContext when its dependencies change ([state, setState])
  const setContext = useCallback(
    updates => {
      setState({ ...state, ...updates })
    },
    [state, setState],
  )

  // here context value is just returning an object, but only re-creating the object when its dependencies change ([state, setContext])
  const getContextValue = useCallback(
    () => ({
      ...state,
      setContext,
    }),
    [state, setContext],
  )
  return (
    <Context.Provider value={getContextValue()}>
      ...

src / App. js

...
import AppContext, { updateProductsContext } from './AppContext'

function App() {
  const [openDrawer, setOpenDrawer] = useState(false)
  const classes = useStyles()
  const {
    page: { title },
    setContext,
  } = useContext(Context)

  useEffect(() => {
    fetch(...)
      .then(...)
      .then(items => {
        setContext(updateProductsContext({ items }))
      })
  }, [])

src / components / DocumentMeta. js

это новый компонент, который вы можете использовать для обновления имен своих страниц в декларативном стиле, уменьшая сложность / избыточность кода в каждом представлении

import React, { useContext, useEffect } from 'react'
import Context, { updatePageContext } from '../Context'

export default function DocumentMeta({ title }) {
  const { page, setContext } = useContext(Context)

  useEffect(() => {
    if (page.title !== title) {
      // TODO use this todo as a marker to also update the actual document title so the browser tab name changes to reflect the current view
      setContext(updatePageContext({ title }))
    }
  }, [title, page, setContext])
  return null
}

иначе использование будет примерно таким: <DocumentMeta title="Whatever Title I Want Here" />


src / pages / Home. js

теперь каждому представлению просто нужно импортировать DocumentMeta и «enum» для обновления заголовка, вместо того, чтобы извлекать контекст и вручную делать это каждый раз .

import { Pages } from '../Context'
import DocumentMeta from '../components/DocumentMeta'

function Home() {
  return (
    <>
      <DocumentMeta title={Pages.home} />
      <h1>WIP</h1>
    </>
  )
}

Примечание. Другие страницы должны повторять то, что делает домашняя страница

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

0 голосов
/ 16 марта 2020

Вот рабочая версия того, что вам нужно.

import React, { useState, useContext, useEffect } from "react";
import "./styles.css";

const Context = React.createContext({});

export default function AppWrapper() {
  // creating a local state
  const [state, setState] = useState({ page: {} });

  return (
    <Context.Provider value={{ state, setState }}> {/* passing state to in provider */}
      <App />
    </Context.Provider>
  );
}

function App() {
  // getting the state from Context
  const { state } = useContext(Context);
  return (
    <>
      <h1>Title: {state.page.title}</h1>
      <Home />
    </>
  );
}

function Home() {
  // getting setter function from Context
  const { setState } = useContext(Context);
  useEffect(() => {
    setState({ page: { title: "Home" } });
  }, [setState]);

  return <p>Hello, World!</p>;
}

Edit long-lake-ct7yu

Подробнее о Справочник по API хуков .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...