Fix components rerendering unnecessarily
Before, some providers (WebRtcProvider, CallProvider...) were rendered conditionally.
This was causing all their children to re-render when the provider rendered.
Instead of using conditional rendering, these providers now use `ConditionalContextProvider`.
It always renders the provider, but sets its value to `initialValue` if the dependencies are not all defined.
If all dependencies are defined, the value is the result of the `useProviderValue` hook.
Changes:
- New file: `ConditionalContextProvider`
- New file: `HookComponent` - Component that calls a hook when mounted and calls a `callback` function with the hook result as argument
- For `WebRtcProvider` and `CallProvider`, use `createOptionalContext` to create the context and `ConditionalContextProvider` to provide their value only when the conditions are met
- In `WebRtcProvider`, set the webRtcConnection to undefined when not in a call
- For all providers, wrap their `value` prop in `useMemo` to avoid unnecessary rerenders
Change-Id: Ide947e216d54599aabc71cf4bd026bd20d6e0daf
diff --git a/client/src/components/CallButtons.tsx b/client/src/components/CallButtons.tsx
index db0f530..9243c33 100644
--- a/client/src/components/CallButtons.tsx
+++ b/client/src/components/CallButtons.tsx
@@ -18,9 +18,9 @@
import { IconButton, IconButtonProps, PaletteColor } from '@mui/material';
import { styled, Theme } from '@mui/material/styles';
-import { ChangeEvent, useContext, useMemo } from 'react';
+import { ChangeEvent, useMemo } from 'react';
-import { CallContext, CallStatus, VideoStatus } from '../contexts/CallProvider';
+import { CallStatus, useCallContext, VideoStatus } from '../contexts/CallProvider';
import {
ExpandableButton,
ExpandableButtonProps,
@@ -88,7 +88,7 @@
});
export const CallingChatButton = (props: ExpandableButtonProps) => {
- const { setIsChatShown } = useContext(CallContext);
+ const { setIsChatShown } = useCallContext();
return (
<CallButton
aria-label="chat"
@@ -102,7 +102,7 @@
};
export const CallingEndButton = (props: ExpandableButtonProps) => {
- const { endCall } = useContext(CallContext);
+ const { endCall } = useCallContext();
return (
<ColoredCallButton
paletteColor={(theme) => theme.palette.error}
@@ -121,7 +121,7 @@
};
export const CallingFullScreenButton = (props: ExpandableButtonProps) => {
- const { setIsFullscreen } = useContext(CallContext);
+ const { setIsFullscreen } = useCallContext();
return (
<CallButton
aria-label="full screen"
@@ -153,7 +153,7 @@
};
const ToggleScreenShareIconButton = (props: IconButtonProps) => {
- const { videoStatus, updateVideoStatus } = useContext(CallContext);
+ const { videoStatus, updateVideoStatus } = useCallContext();
return (
<ToggleIconButton
@@ -169,7 +169,7 @@
};
const useMediaDeviceExpandMenuOptions = (kind: MediaDeviceKind): ExpandMenuRadioOption[] | undefined => {
- const { currentMediaDeviceIds, mediaDevices } = useContext(CallContext);
+ const { currentMediaDeviceIds, mediaDevices } = useCallContext();
const options = useMemo(
() =>
@@ -224,7 +224,7 @@
};
const ToggleAudioCameraIconButton = (props: IconButtonProps) => {
- const { isAudioOn, setIsAudioOn } = useContext(CallContext);
+ const { isAudioOn, setIsAudioOn } = useCallContext();
return (
<ToggleIconButton
IconOn={MicroIcon}
@@ -250,7 +250,7 @@
};
const ToggleVideoCameraIconButton = (props: IconButtonProps) => {
- const { videoStatus, updateVideoStatus } = useContext(CallContext);
+ const { videoStatus, updateVideoStatus } = useCallContext();
return (
<ToggleIconButton
IconOn={VideoCameraIcon}
@@ -266,7 +266,7 @@
// Calling pending/receiving interface
export const CallingCancelButton = (props: IconButtonProps) => {
- const { endCall } = useContext(CallContext);
+ const { endCall } = useCallContext();
return (
<ColoredCallButton
@@ -282,7 +282,7 @@
};
export const CallingAnswerAudioButton = (props: IconButtonProps) => {
- const { acceptCall, callStatus } = useContext(CallContext);
+ const { acceptCall, callStatus } = useCallContext();
return (
<ColoredCallButton
@@ -299,7 +299,7 @@
};
export const CallingAnswerVideoButton = (props: IconButtonProps) => {
- const { acceptCall, callStatus } = useContext(CallContext);
+ const { acceptCall, callStatus } = useCallContext();
return (
<ColoredCallButton
disabled={callStatus === CallStatus.Connecting || callStatus === CallStatus.Loading}
@@ -315,7 +315,7 @@
};
export const CallingRefuseButton = (props: IconButtonProps) => {
- const { endCall } = useContext(CallContext);
+ const { endCall } = useCallContext();
return (
<ColoredCallButton
aria-label="refuse call"