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

标签 javascript reactjs typescript react-hooks rerender

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

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 不关心加载。但是它包含在上下文中,所以组件重新渲染了 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) 并且我没有重新渲染。

关于javascript - 由于上下文中未使用的属性,组件重新呈现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56602872/

相关文章:

javascript - 为什么我们需要 res.on ('data' ... 为 POST 定义,即使我们没有对数据做任何事情?

reactjs - 在 React 16 中启用异步渲染

angular - 以 Angular 访问此对象时未定义

Angular - 管道函数中断执行流程

typescript - 如何在 typescript 中编写 Helper 类?

javascript - 悬停时显示前一个元素

javascript - jquery - onclick 内容向下滑动

javascript - Three.js 中的插值曲面

reactjs - React Dumb 组件 Typescript 错误 : Element is not a constructor function for JSX elements, 属性 'render' 丢失

reactjs - 调整大小时 Ref 返回 null