React TS-如何将React上下文用于状态管理
#javascript #react #前端 #cleancode

上下文在React development中起关键作用。它们可以有效地管理组件跨组件的共享状态和数据,从而促进了应用程序不同部分之间的无缝通信。了解如何利用上下文的力量对于构建可靠和可维护的反应应用是至关重要的。


什么是上下文?

React中的上下文是一种机制,可有效地通过组件树传递数据,而无需在每个级别手动传递道具。将它们视为应用程序数据的全球胸部。

用日常的例子说明这一点,请想象您正在计划一次家庭旅行。您的家人代表您应用程序的不同组成部分。您可以在客厅中使用白板(上下文),而不是单独致电每个家庭成员为他们提供旅行详细信息,每个人都可以看到计划。
现在,如果您更新白板上的出发时间,每个人都会立即知道更改。

在React中,这类似于在上下文中更新数据,并且订阅该上下文的任何组件都将自动接收更新的信息,而无需在组件之间进行直接通信。它简化了数据共享并确保整个应用程序的一致性,就像您的家庭旅行计划一样。


优点

上下文在React开发方面具有许多优势,使其成为建立可扩展和可维护应用程序的必不可少的工具。一些关键好处包括:

  • 简化的数据共享:上下文消除了对组件树不同级别的组件之间的数据。
  • 清洁器代码:它们通过将数据关注与演示问题分开,从而鼓励清洁,模块化代码,从而产生更可维护和可理解的代码库。
  • 全球状态管理:上下文在管理全球状态方面表现出色,确保关键应用程序数据在整个应用程序中保持一致且易于访问。
  • 改进的性能:通过仅智能更新依赖更改数据的组件,上下文有助于优化性能,减少不必要的重新订阅。
  • 代码可读性:使用上下文进行状态管理增强代码可读性,使您更容易掌握应用程序的结构和流程。

将上下文纳入您的React项目,使您能够构建更有效,可维护和可扩展的应用程序,最终导致更好的开发体验并提高用户满意度。


普通案例

挑战

想象我们正在构建一个天气应用程序,该应用程序显示不同城市的当前天气状况。每个城市的天气数据包括其名称,温度和天气描述。
这是我们应用程序的结构:

Project architecture

现在,如果我们想在SearchBar.tsx中获取数据并在CurrentWeather.container.txWeekWeather.container.tsx中显示它,而无需上下文,我们需要在我们的App.tsx的顶部制作一个状态:

// App.tsx

export type WeatherWeekData = {
  temperature: number
}

export type WeatherData = {
  town: string
  current: WeatherWeekData
  week: WeatherWeekData[]
}

function App() {
  const [weatherData, setWeatherData] = useState<WeatherData | null>(null)

  return (
    <div className="App">
      <Header />
      <SearchBar setWeatherData={setWeatherData} />
      {weatherData && <CurrentWeather town={weatherData.town} temperature={weatherData.current.temperature} />}
      {weatherData && <WeekWeather town={weatherData.town} week={weatherData.week} />}
      <Footer />
    </div>
  )
}

export default App
// SearchBar.tsx

type SearchBarProps = {
  setWeatherData: (weatherData: WeatherData) => void
}

export const SearchBar = ({ setWeatherData }: SearchBarProps) => {
  const [searchTerm, setSearchTerm] = useState<string>('')

  const handleOnInputChange = (event: ChangeEvent<HTMLInputElement>) => {
    // wait 1s delay before set the term and fetch data
    setTimeout(() => {
      setSearchTerm(event.target.value)
    }, 1000)
  }
  const fetchData = (term: string) => {
    fetch('https://mysuperAPI.com/search?term=' + term)
      .then((response) => response.json())
      .then((data) => setWeatherData(data))
  }

  useEffect(() => {
    fetchData(searchTerm)
  }, [searchTerm])

  return (
    <div>
      <input placeholder="Find your town" value={searchTerm} onChange={handleOnInputChange} />
    </div>
  )
}
// CurrentWeather.container.tsx

type CurrentWeatherProps = {
  town: string
  temperature: number
}

export const CurrentWeather = ({ town, temperature }: CurrentWeatherProps) => {
  return (
    <div>
      <h1>Current Weather</h1>

      <div>
        <h2>{town}</h2>
        <p>{temperature} °F</p>
      </div>
    </div>
  )
}
// WeekWeather.container.tsx

type WeekWeatherProps = {
  town: string
  week: WeatherWeekData[]
}

export const WeekWeather = ({ town, week }: WeekWeatherProps) => {
  const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

  return (
    <div>
      <h1>WeekWeather Weather</h1>

      <div>
        <h2>{town}</h2>

        <div>
          {week.map((day, index) => (
            <div>
              <h3>Day : {days[index]}</h3>
              <p>{day.temperature} °F</p>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

正如我们所看到的,我们需要共享状态和固定状态,以确保其他姐妹组件具有数据。不幸的是,该解决方案无法维护。到处都是道具,数据并不是真正集中的。


解决方案

为了获得清洁代码,我们将创建上下文!其目的是存储数据,以便所有组件都可以访问。

结构:
创建一个文件夹src/contexts/并在上下文的文件夹中创建3个文件:

Contexts architecture

解释

  • 天气动作:我们的行为清单除外:setweather,setfavoriteTown等
  • 气象提供商:这将围绕您需要访问数据的一部分。它还管理国家本身。
  • 降温器:他管理各种动作,因此,如果您想修改或添加数据,则取决于他。

创建所有文件:

动作
// weather.actions.ts

import { WeatherData } from './weather.reducer'

export enum EWeatherActions {
  SET_WEATHER = 'SET_WEATHER'
}

type SetWeather = {
  type: EWeatherActions.SET_WEATHER
  payload: WeatherData
}

export const setWeather = (args: WeatherData): SetWeather => ({
  type: EWeatherActions.SET_WEATHER,
  payload: args
})

export type WeatherActions = SetWeather

很高兴知道:

  • setWeather是如果要将数据添加到上下文中,我们应该调用的操作。
  • 我们导出输入还原器的类型。
减速器
// weather.reducer.ts

import { Reducer } from 'react'
import { EWeatherActions, WeatherActions } from './weather.actions'

export type WeatherWeekData = {
  temperature: number
}

export type WeatherData = {
  town: string
  current: WeatherWeekData | null
  week: WeatherWeekData[]
}

export type WeatherState = {
  weather: WeatherData | null
}

export const initialState: WeatherState = {
  weather: null
}

export const weatherReducer: Reducer<WeatherState, WeatherActions> = (state = initialState, action) => {
  switch (action.type) {
    case EWeatherActions.SET_WEATHER:
      return {
        ...state,
        ...action.payload
      }
    default:
      return { ...state }
  }
}

很高兴知道:

  • 有很多类型,但重要的想法是initialStateweatherReducer
  • initialStateâ:名称,是我们上下文的初始状态。我们Juse将天气对象带有我们的数据。
  • weatherReducer:这是一个简单的开关 /情况。< / li>
提供者
// weather.provider.ts

import { createContext, Dispatch, ReactNode, useContext, useMemo, useReducer } from 'react'
import { initialState, weatherReducer, WeatherState } from './weather.reducer'
import { WeatherActions } from './weather.actions'

type WeatherContext = WeatherState & {
  dispatch: Dispatch<WeatherActions>
}

const weatherContext = createContext<WeatherContext>({ ...initialState, dispatch: () => {} })

export const useWeatherContext = () => useContext(weatherContext)

type WeatherProviderProps = {
  children: ReactNode
}

export const WeatherProvider = ({ children }: WeatherProviderProps) => {
  const [state, dispatch] = useReducer(weatherReducer, initialState)

  const value: WeatherContext = useMemo(() => ({ ...state, dispatch }), [state])
  return <weatherContext.Provider value={value}>{children}</weatherContext.Provider>
}

很高兴知道:

  • weathercontext:并不重要的变量,这是简单地制作WeatherProvider
  • useWeatherContext:这是一个昵称,称为我们的“ usecontext”
  • 的快捷方式
  • WeatherProvider:我们的状态,我们需要包围应用程序需要数据限制访问并增加 performance
  • 的部分。

使用我们的上下文!

我们的新app.tsx:

// App.tsx

function App() {
  return (
    <div className="App">
      <Header />
      <WeatherProvider>
        <SearchBar />
        <CurrentWeather />
        <WeekWeather />
      </WeatherProvider>
      <Footer />
    </div>
  )
}

export default App

我们已经删除了所有道具,并与WeatherProvider一起包围了天气部分,以与。

共享数据
设置数据:
// SearchBar.tsx

export const SearchBar = () => {
  const { dispatch: dispatchWeather } = useWeatherContext()
  const [searchTerm, setSearchTerm] = useState<string>('')

  const handleOnInputChange = (event: ChangeEvent<HTMLInputElement>) => {
    // wait 1s delay before set the term and fetch data
    setTimeout(() => {
      setSearchTerm(event.target.value)
    }, 1000)
  }
  const fetchData = (term: string) => {
    fetch('https://mysuperAPI.com/search?term=' + term)
      .then((response) => response.json())
      .then((data) => dispatchWeather(setWeather(data)))
  }

  useEffect(() => {
    fetchData(searchTerm)
  }, [searchTerm])

  return (
    <div>
      <input placeholder="Find your town" value={searchTerm} onChange={handleOnInputChange} />
    </div>
  )
}

在此文件中,我们从useWeatherContext获取dispatch。调度是一个允许您使用我们定义的操作之一的函数。在这里,我们进行调度并重命名为Dispatchweather。重命名调度会在我们有很多上下文和调度时更容易调试。

使用数据:
// CurrentWeather.container.tsx

export const CurrentWeather = () => {
  const { weather } = useWeatherContext()

  if (!weather) return <div>Please select a town.</div>

  return (
    <div>
      <h1>Current Weather</h1>

      <div>
        <h2>{weather.town}</h2>
        <p>{weather.current.temperature} °F</p>
      </div>
    </div>
  )
}
// WeekWeather.container.tsx

export const WeekWeather = () => {
  const { weather } = useWeatherContext()
  const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

  if (!weather) return <div>Please select a town.</div>

  return (
    <div>
      <h1>WeekWeather Weather</h1>

      <div>
        <h2>{weather.town}</h2>

        <div>
          {weather.week.map((day, index) => (
            <div>
              <h3>Day : {days[index]}</h3>
              <p>{day.temperature} °F</p>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

就是这样!我们创建并使用了自己的干净背景!恭喜。


走得更远

一旦掌握了React上下文的基础知识,您就可以通过探索高级主题将应用程序开发提高到一个新的水平:

  • localstorage带有上下文:将上下文的力量与localstorage的力量结合到持续应用程序状态。这对于维护用户偏好(例如主题选择,用户设置,甚至是用户购物车的最后状态)特别有用。通过将上下文与LocalStorage链接,您可以确保在会话之间保留特定于用户的数据。这通过提供连续性和个性化来增强用户体验。
  • 与Redux集成:虽然React上下文非常适合管理本地组件级别,但Redux是一个强大的状态管理库,在整个应用程序中都擅长管理全球状态。您可以通过将Redux用于总体应用程序状态和上下文来利用这两者来利用更具体的组件级状态管理。这种混合方法提供了两全其美的最佳,使您可以在组件之间有效管理和共享数据,同时将全球状态商店保留用于复杂的应用程序级数据。
  • 测试和调试:探索使用上下文的测试和调试应用程序的工具和技术。诸如React测试库和Redux DevTools之类的库对于确保代码的可靠性和性能非常有价值。

结论

总而言之,React上下文对于您的应用程序中的有效数据共享至关重要。他们简化代码,有效地管理全球状态并提高性能。在一个实际的示例中,我们看到了使用上下文如何大幅度清理您的代码。通过掌握上下文,您将构建更有效,可维护的应用程序而不会失去读者的兴趣。

如果您喜欢本教程,请考虑关注我以获取更多有用的内容。您的支持是极大的赞赏!谢谢!

X _brdnicolas