Používání kontextového API v Reactu (Hooks and Classes)

React Context API je způsob, jak v podstatě vytvořit globální proměnné, které lze předávat v aplikaci React. Toto je alternativa k „vrtání rekvizit“ nebo předávání rekvizit od prarodičů rodičům k dítěti a tak dále. Kontext je často nabízen jako jednodušší a lehčí řešení pro použití Redux pro správu státu. Sám jsem Redux ještě nepoužil, ale pokaždé, když použiji kontextové API Reactu, musím to vyhledat, protože mi to nepřipadá samozřejmé.

Nechám zde několik stručných a stručných kroků, jak začít s Contextem.

Předpoklad

  • Pokud ještě neznáte React nebo React Hooks, přečtěte si článek Začínáme s React nebo Sestavení aplikace React s Hooks.

Vytvořit kontext

Představte si, že mám nějaké informace, které chci být dostupné kdekoli nebo kdekoli v aplikaci React. Motiv lze implementovat pomocí Context – například na tomto webu mám Context, který slouží ke třem tématům:tmavý režim, světlý režim a režim MS-DOS (na stránce 404). V tomto jednoduchém příkladu použiji protokol v uživateli.

Vytvořím kontext a nazvu jej UserContext . To mi také dá UserContext.Provider a UserContext.Consumer . Co tyto dvě složky dělají, je jednoduché:

  • Poskytovatel - Komponenta, která poskytuje hodnotu
  • Spotřebitel - Komponenta, která spotřebovává hodnotu

Takže to vytvořím pomocí React.createContext() v novém souboru s názvem UserContext.js .

src/UserContext.js
import React from 'react'

const UserContext = React.createContext()

export const UserProvider = UserContext.Provider
export const UserConsumer = UserContext.Consumer

export default UserContext

Zde předávám prázdnou hodnotu objektu, abych reprezentoval, že tato data možná vyplním později pomocí volání API. Můžete to předem vyplnit libovolnými údaji, pokud data nenačítáte prostřednictvím rozhraní API.

React.createContext(true)

Poskytování kontextu

Poskytovatel musí vždy existovat jako obal kolem nadřazeného prvku, bez ohledu na to, jak se rozhodnete využívat hodnoty. Zabalím celých App součást v Provider . Právě vytvářím nějakou hodnotu (user ) a předat jej jako Provider hodnota prop.

src/App.js
import React from 'react'
import HomePage from './HomePage'
import { UserProvider } from './UserContext'

function App() {
  const user = { name: 'Tania', loggedIn: true }

  return (
    <UserProvider value={user}>
      <HomePage />
    </UserProvider>
  )
}

Nyní bude mít přístup k user každé dítě, vnouče, pravnuk a tak dále jako rekvizita. Bohužel získání této hodnoty je o něco složitější než její pouhé získání, jako byste mohli s this.props nebo this.state .

Kontext konzumace

Způsob, jakým poskytujete kontext, je stejný pro třídu a funkční komponenty, ale jeho použití je u obou trochu odlišné.

Komponenta třídy

Nejběžnějším způsobem přístupu ke kontextu z komponenty třídy je statický contextType . Pokud potřebujete hodnotu z kontextu mimo render , nebo v metodě životního cyklu, budete jej používat tímto způsobem.

src/HomePage.js (příklad třídy)
import React, { Component } from 'react'
import UserContext from './UserContext'

class HomePage extends Component {
  static contextType = UserContext

  componentDidMount() {
    const user = this.context

    console.log(user) // { name: 'Tania', loggedIn: true }
  }

  render() {
    return <div>{user.name}</div>
  }
}

Tradičním způsobem, jak získat kontextové hodnoty, bylo zabalení podřízené komponenty do Consumer . Odtud byste měli mít přístup k hodnotě prop jako props . Můžete to stále vidět, ale jde spíše o starší způsob přístupu ke kontextu.

src/HomePage.js (příklad třídy)
import React, { Component } from 'react'
import { UserConsumer } from './UserContext'

class HomePage extends Component {
  render() {
    return (
      <UserConsumer>
        {(props) => {
          return <div>{props.name}</div>
        }}
      </UserConsumer>
    )
  }
}

Funkční komponenta a háčky

Pro funkční komponenty použijete useContext , jako v příkladu níže. Toto je ekvivalent static contextType .

src/HomePage.js
import React, { useContext } from 'react'
import UserContext from './UserContext'

export const HomePage = () => {
  const user = useContext(UserContext)

  return <div>{user.name}</div>
}

Aktualizace kontextu

Aktualizace kontextu se příliš neliší od aktualizace běžného stavu. Můžeme vytvořit obalovou třídu, která obsahuje stav kontextu a prostředky k jeho aktualizaci.

src/UserContext.js
import React, { Component } from 'react'

const UserContext = React.createContext()

class UserProvider extends Component {
  // Context state
  state = {
    user: {},
  }

  // Method to update state
  setUser = (user) => {
    this.setState((prevState) => ({ user }))
  }

  render() {
    const { children } = this.props
    const { user } = this.state
    const { setUser } = this

    return (
      <UserContext.Provider
        value={{
          user,
          setUser,
        }}
      >
        {children}
      </UserContext.Provider>
    )
  }
}

export default UserContext

export { UserProvider }

Nyní můžete aktualizovat a zobrazit uživatele pomocí kontextové metody.

import React, { Component } from 'react'
import UserContext from './UserContext'

class HomePage extends Component {
  static contextType = UserContext

  render() {
    const { user, setUser } = this.context

    return (
      <div>
        <button
          onClick={() => {
            const newUser = { name: 'Joe', loggedIn: true }

            setUser(newUser)
          }}
        >
          Update User
        </button>
        <p>{`Current User: ${user.name}`}</p>
      </div>
    )
  }
}

Podle mého názoru je největší nevýhodou Context API s třídami, že nemůžete použít více statických contextTypes v jedné složce. To vede k nutnosti mít jeden opravdu velký kontext pro všechny globální stavy v aplikaci, takže pro velkou aplikaci to nestačí. Metoda vytvoření obalu pro Context je také obtížně testovatelná.

Závěr

Abych to shrnul:

  • Použijte const ___Context = React.createContext() vytvořit kontext.
  • Zatáhněte za ___Context.Provider a ___Context.Consumer z ___Context
  • Zabalit Provider kolem vaší nadřazené komponenty.
  • Třída může spotřebovávat s static contextType = ___Context
  • Funkční komponenta může mít spotřebu const x = useContext(___Context)

Doufám, že to pomůže!