由于上下文中未使用的属性,组件重新呈现



我有一个AsyncContext,允许我启动/停止任何类型的异步计算。在后台,它管理着一个全局加载程序和一个小吃店。

export type Context = {
  loading: boolean
  start: () => void
  stop: (message?: string) => void
}
const defaultContext: Context = {
  loading: false,
  start: noop,
  stop: noop,
}
export const AsyncContext = createContext(defaultContext)

这里的消费者:

const MyChild: FC = () => {
  const {start, stop} = useContext(AsyncContext)
  async function fetchUser() {
    try {
      start()
      const res = await axios.get('/user')
      console.log(res.data)
      stop()
    } catch (e) {
      stop('Error: ' + e.message)
    }
  }
  return (
    <button onClick={fetchData}>
      Fetch data
    </button>
  )
}

如您所见,MyChild不在乎loading.但它包含在上下文中,因此组件免费重新渲染 2 次。

为了防止这种情况,我的第一个尝试是将我的组件一分为二,并使用memo

type Props = {
  start: AsyncContext['start']
  stop: AsyncContext['stop']
}
const MyChild: FC = () => {
  const {start, stop} = useContext(AsyncContext)
  return <MyChildMemo start={start} stop={stop} />
}
const MyChildMemo: FC<Props> = memo(props => {
  const {start, stop} = props
  async function fetchUser() {
    try {
      start()
      const res = await axios.get('/user')
      console.log(res.data)
      stop()
    } catch (e) {
      stop('Error: ' + e.message)
    }
  }
  return (
    <button onClick={fetchData}>
      Fetch data
    </button>
  )
})

它有效,但我不想拆分所有使用 AsyncContext 的孩子.

第二次尝试是直接在 JSX 上使用useMemo

const MyChild: FC = () => {
  const {start, stop} = useContext(AsyncContext)
  async function fetchUser() {
    try {
      start()
      const res = await axios.get('/user')
      console.log(res.data)
      stop()
    } catch (e) {
      stop('Error: ' + e.message)
    }
  }
  return useMemo(() => (
    <button onClick={fetchData}>
      Fetch data
    </button>
  ), [])
}

也可以工作,它更精简,但我不确定这是否是一种好的做法。

我的两种方法中的任何一种是否正确?如果没有,你会有什么建议?

我想

我找到了最好的方法,这要归功于 https://kentcdodds.com/blog/how-to-use-react-context-effectively:在两个上下文中分离上下文。一个用于国家,一个用于调度:

type StateContext = boolean
type DispatchContext = {
  start: () => void
  stop: (message?: string | void) => void
}
export const AsyncStateContext = createContext(false)
export const AsyncDispatchContext = createContext({start: noop, stop: noop})

如果使用者不需要状态,我只需添加const {start, stop} = useContext(AsyncDispatchContext),并且没有重新渲染。

相关内容

  • 没有找到相关文章