Skip to content

两种组件间通信方式

使用Context进行组件通信

创建context文件夹,并创建menuContext文件,用于创建上下文。

tsx
import React from 'react'

export const menuContext = React.createContext({
  setVisible: (visible: boolean) => {}
})

修改Icon.tsx组件,使其接受点击事件。

tsx
import c from 'classnames'
import React from 'react'

interface Props {
  className?: string
  name: string
  onClick?: (e: React.MouseEvent) => void
}
export const Icon: React.FC<Props> = ({ name, className, onClick }) => {
  return (
    <svg className={c(className, 'j-icon')} onClick={onClick}>
      <use xlinkHref={`#${name}`}></use>
    </svg>
  )
}

创建TopMenu.tsx文件

tsx
export const TopMenu: React.FC = () => {
  return (
    <div fixed top-0 left-0 bg-red>TopMenu </div>
  )
}

然后在ItemsPages.tsx文件中使用menuContext上下文进行注入。

tsx
import styled from 'styled-components'
import { useState } from 'react'
import { AddItemFloatButton } from '../components/AddItemFloatButton'
import { TimeRangePicker } from '../components/TimeRangePicker'
import type { TimeRange } from '../components/TimeRangePicker'
import { TopNav } from '../components/TopNav'
import { menuContext } from '../context/menuContext'
import { TopMenu } from '../components/TopMenu'
import { ItemsList } from './ItemsPage/ItemsList'
import { ItemsSummary } from './ItemsPage/ItemsSummary'

const Div = styled.div`
  background: linear-gradient(0deg, rgba(143,76,215,1) 0%, rgba(92,51,190,1) 100%);
`

export const ItemsPage: React.FC = () => {
  const [timeRange, setTimeRange] = useState<TimeRange>('thisMonth')
  const [items] = useState<Item[]>([
    {
      id: 1,
      kind: 'incomes',
      amount: 1000,
      user_id: 1,
      tag_ids: [1],
      happen_at: '2021-01-01T00:00:00.000Z',
      created_at: '2021-01-01T00:00:00.000Z',
      updated_at: '2021-01-01T00:00:00.000Z',
    }, {
      id: 2,
      kind: 'incomes',
      amount: 1000,
      user_id: 1,
      tag_ids: [1],
      happen_at: '2021-01-01T00:00:00.000Z',
      created_at: '2021-01-01T00:00:00.000Z',
      updated_at: '2021-01-01T00:00:00.000Z',
    }
  ])
  const [visible, setVisible] = useState(false)
  return (
    <div>
      <menuContext.Provider value={{ setVisible }}>
        <Div>
          <TopNav />
          <TimeRangePicker selected={timeRange} onSelected={setTimeRange} />
        </Div>
        <ItemsSummary />
        <ItemsList items={items}/>
        <AddItemFloatButton />
        {visible ? <TopMenu /> : null}
      </menuContext.Provider>
    </div>
  )
}

然后在TopNav.tsx中使用useContext接受menuContext对象,修改visble的值。

tsx
import { useContext } from 'react'
import { menuContext } from '../context/menuContext'
import { Icon } from './Icon'

interface Props {
  title?: string
}
export const TopNav: React.FC<Props> = ({ title = '山竹记账' }) => {
  const { setVisible } = useContext(menuContext)
  return (
    <div text-white flex items-center pt-24px pb-8px px-24px>
      <Icon name="menu" className="w-24px h-24px mr-16px"
      onClick={ () => { setVisible(true) }} />
      <h1 text-24px>{title}</h1>
    </div>
  )
}

使用zustand进行组件通信

使用zustand数据状态管理来进行组件之间的通信,详细代码见链接

封装useMenuStore

tsx
import create from 'zustand'

interface Menu {
  visible: boolean
  setVisible: (visible: boolean) => void
}

export const useMenuStore = create<Menu>((set, get) => ({
  visible: false,
  setVisible: (visible) => {
    set({ visible })
  }
}))

然后在ItemsPages.tsx文件中使用useMenuStorevisible的值。

tsx
import styled from 'styled-components'
import { useState } from 'react'
import { AddItemFloatButton } from '../components/AddItemFloatButton'
import { TimeRangePicker } from '../components/TimeRangePicker'
import type { TimeRange } from '../components/TimeRangePicker'
import { TopNav } from '../components/TopNav'
import { TopMenu } from '../components/TopMenu'
import { useMenuStore } from '../stores/useMenuStore'
import { ItemsList } from './ItemsPage/ItemsList'
import { ItemsSummary } from './ItemsPage/ItemsSummary'

const Div = styled.div`
  background: linear-gradient(0deg, rgba(143,76,215,1) 0%, rgba(92,51,190,1) 100%);
`

export const ItemsPage: React.FC = () => {
  const [timeRange, setTimeRange] = useState<TimeRange>('thisMonth')
  const [items] = useState<Item[]>([
    {
      id: 1,
      kind: 'incomes',
      amount: 1000,
      user_id: 1,
      tag_ids: [1],
      happen_at: '2021-01-01T00:00:00.000Z',
      created_at: '2021-01-01T00:00:00.000Z',
      updated_at: '2021-01-01T00:00:00.000Z',
    }, {
      id: 2,
      kind: 'incomes',
      amount: 1000,
      user_id: 1,
      tag_ids: [1],
      happen_at: '2021-01-01T00:00:00.000Z',
      created_at: '2021-01-01T00:00:00.000Z',
      updated_at: '2021-01-01T00:00:00.000Z',
    }
  ])
  const { visible } = useMenuStore()
  return (
    <div>
      <Div>
        <TopNav />
        <TimeRangePicker selected={timeRange} onSelected={setTimeRange} />
      </Div>
      <ItemsSummary />
      <ItemsList items={items}/>
      <AddItemFloatButton />
      {visible ? <TopMenu /> : null}
    </div>
  )
}

并在TopNav.tsx中使用useMenuStoresetVisible方法修改visible的值。

tsx
import { useMenuStore } from '../stores/useMenuStore'
import { Icon } from './Icon'

interface Props {
  title?: string
}
export const TopNav: React.FC<Props> = ({ title = '山竹记账' }) => {
  const { visible, setVisible } = useMenuStore()
  return (
    <div text-white flex items-center pt-24px pb-8px px-24px>
      <Icon name="menu" className="w-24px h-24px mr-16px"
      onClick={ () => setVisible(!visible) } />
      <h1 text-24px>{title}</h1>
    </div>
  )
}