web-dev-qa-db-ja.com

Reactプロバイダーの複数の値を持つコンテキストをフックする方法

いくつかのグローバルな価値と機能を共有するための最良の方法は何ですか?

今私はそれらすべての中に1つのContextProviderを持っています:

<AllContext.Provider
  value={{
    setProfile, // second function that changes profile object using useState to false or updated value
    profileReload, // function that triggers fetch profile object from server
    deviceTheme, // object
    setDeviceTheme, // second function that changes theme object using useState to false or updated value
    clickEvent, // click event
    usePopup, // second function of useState that trigers some popup
    popup, // Just pass this to usePopup component
    windowSize, // manyUpdates on resize (like 30 a sec, but maybe can debounce)
   windowScroll // manyUpdates on resize (like 30 a sec, but maybe can debounce)
  }}
>

しかし、悲しいように docs :コンテキストは参照IDを使用していつ再レンダリングするかを決定するため、プロバイダーの親が再レンダリングすると、コンシューマーで意図しないレンダリングをトリガーする可能性のある問題がいくつかあります。たとえば、新しいオブジェクトは常に値に対して作成されるため、次のコードは、プロバイダーが再レンダリングするたびにすべてのコンシューマーを再レンダリングします。

これは悪いです:

<Provider value={{something: 'something'}}>

これで結構です:

this.state = {
      value: {something: 'something'},
    };
<Provider value={this.state.value}>

将来的には最大30のコンテキストプロバイダーが存在すると思いますが、あまり友好的ではありません:/

では、このグローバル値と関数をコンポーネントに渡すにはどうすればよいですか?できます

  1. すべてに対して個別のcontextProviderを作成します。
  2. プロファイルやその機能、テーマ、機能など、一緒に使用されているものをグループ化します(参照IDはどうですか?)
  3. 多分グループ自体が機能しないので機能するだけですか?参照アイデンティティはどうですか?)
  4. 他の最も簡単な方法は?

プロバイダーで使用するものの例:

// Resize
  const [windowSize, windowSizeSet] = useState({
    innerWidth: window.innerWidth,
    innerHeight: window.innerHeight
  })
// profileReload
const profileReload = async () => {
    let profileData = await fetch('/profile')
    profileData = await profileData.json()

    if (profileData.error)
      return usePopup({ type: 'error', message: profileData.error })

    if (localStorage.getItem('deviceTheme')) {
      setDeviceTheme(JSON.parse(localStorage.getItem('deviceTheme'))) 
    } else if (profileData.theme) {
      setDeviceTheme(JSON.parse(JSON.stringify(profileData.theme)))
    } else {
      setDeviceTheme(settings.defaultTheme) 
    }
    setProfile(profileData)
  }

// Click event for menu close if clicked outside somewhere and other
const [clickEvent, setClickEvent] = useState(false)
const handleClick = event => {
  setClickEvent(event)
}
// Or in some component user can change theme just like that
setDeviceTheme({color: red})
5
RTW

あなたはまだそれらを組み合わせることができます!パフォーマンスが心配な場合は、オブジェクトを先に作成できます。使用する値が変化するかどうかはわかりませんが、変化しない場合は非常に簡単です。

state = {
  allContextValue: {
    setProfile,
    profileReload,
    deviceTheme,
    setDeviceTheme,
    clickEvent,
    usePopup,
    popup,
    windowSize
  }
}

render() {
  return <AllContext.Provider value={this.state.allContextValue}>...</AllContext>;
}

ただし、実行する必要がある値を更新したいときはいつでも、次のようにします。

this.setState({
  allContextValue: {
    ...this.state.allContextValue,
    usePopup: true,
  },
});

これはパフォーマンスが高く、比較的簡単でもあります:)それらを分割すると少しスピードアップするかもしれませんが、実際に遅くなるとすぐに、そして多くの消費者。

それでも、あなたの価値があまり変わらなければ、心配することは何もありません。

0
Michiel Dral