Optimization Suggestions for NotificationProvider Component in Next.js Project
Environment
- Path:
./providers/notificationProvider.tsx
Description
During a code review of the NotificationProvider component, two primary concerns were identified that may impact performance and client-side rendering behavior. Addressing these concerns could potentially enhance the component's efficiency
Concerns
1. Lack of Client-Side Rendering Indication
The component manages state and side effects, which are relevant primarily to client-side execution. However, there's no explicit indication or control to ensure that this code executes only on the client side. This absence could lead to unnecessary execution paths or errors during server-side rendering processes in Next.js.
2. UseEffect Dependency on Object State
The implementation uses notification, an object, as a dependency in a useEffect hook. Given that objects in JavaScript are reference types, this setup can lead to unnecessary executions of the effect. The effect, which clears a timeout based on notification?.duration, will re-run on any change to notification — even if the actual content remains unchanged. This behavior can introduce performance inefficiencies.
Suggested Improvements
1. Implement Explicit Client-Side Execution Control
To ensure that the component's logic executes only in environments where it's applicable, consider utilizing Next.js's client-side rendering capabilities more explicitly. This control could be achieved through dynamic imports with { ssr: false } or by using client-side hooks that defer rendering until the client environment is confirmed.
2. Optimize useEffect Dependencies
To mitigate unnecessary effect executions tied to the notification state, it may be beneficial to:
- Decompose the state and use a more primitive aspect (like
notification?.duration) as the dependency. see - react docs
useEffect(() => {
const timeoutId = setTimeout(() => dispatch(null), notification?.duration);
return () => clearTimeout(timeoutId);
}, [notification?.duration]);
- Implement a custom hook for deep comparison of the relevant parts of
notification, or memoize the state where applicable to prevent the effect from running more than necessary.
@ra2u18 i'm not very sure about the second point but on the first point the notificationProvider component is only being used within parent components that have the react 'use client';
Wow good catch ! @ra2u18 feel free to open an pr.
Hello @AugustinMauroy. I would like to work on this issue.
Hello @Aryakoste you can open pr if you want
Ok
This issue doesnt make any sense to me. React Effects are only ever executed on the client-side; There shouldn't be any window check within a useEffect statement.
Could you please tell me from where did you find these claims? Any source for this?
@ra2u18 i'm not very sure about the second point but on the first point the notificationProvider component is only being used within parent components that have the react 'use client';
Genuinely speaking this whole issue feels like generated bt ChatGPT.
@ovflowd thanks for your unnecessary ironic answer, however, not sure where I said you need a "window check" in the useEffect statement. I already sourced react documentation in the issue contents, please make yourself some time to find it out.
Secondly, not sure why would you prefer to make (the parent) "./layouts/Base.tsx" a client component instead of delegating this responsability to NotificationProvider which is actually the one that's causing the rehydration.
Even though you don't perform work on the server (in the case of Base.tsx, like fetching data or accessing db) it's still a good practice to leave it as RSC if you don't handle any client interactivity.
PS: let's practice kindness, drop the guesswork
Look, your points on the opening issue aren't really applicable here as:
- Using notification?.duration as a dependency still has a coerce null check, which, will result in the effect being executed anyways.
- This effect doesn't cause any rerender at all as it does not update any state. It will only cause a rerender during the timeout being triggered, but that happens asynchronously as an async hook.
- The base layout is a client component for numerous reasons, the notification provider might be one of them. Making the base layout a client or server component honestly has no measurable effect in bundle size or performance
- The notification state is an object but that isn't an issue. The state itself will never only have a partial change, the only state action for that specific state will always create a new object. Using an object as a state in this situation isn't a problem, because the object is not a component property but a state, which is memoized and will be the same object on every outer rerender of the component calling the provider. Which in this case in general are 0, as the base layout only has one render at all and only rerenders when the notification updates.
- The Base layout cannot in this case be a server component and having the inner content of the provider as a client component, since the rules of Next.js where a server component -> client component (the provider) cannot slot other server children the way it is stated there. This was already tested beforehands.
- Any of the changes here have negligible if none performance gains.
So excuse me for being a bit sarcastic when I see an issue being opened that genuinely has no purpose, which with all due respect sounds generated by ChatGPT, that quotes react documentation for situations that are not applicable here.
Sure, I might be wrong and you did spend real time into writing this, in which situation, I apologise. But unfortunately the statements on this issue are still incorrect and have no basis, although in general they would make sense, they are not applicable here.
understood @ovflowd , thanks for further clarifications! This was a suggestion - as the title says - therefore, if there's negligible to none performance gains, I will close this issue.
I understand you had, I assume, good intents when opening this issue. As mentioned before, the technicality behind it, is in general sound, but just not applicable here.
I would recommend before opening an issue, of this kind, which is a said "optimisation" of the current code, to actually validate that these suggestions are applicable, work, and actually provide measurable results.
Otherwise you're spending time of maintainers and newcomers that want to pick issues to work on.
Have a good one.