Sometimes people use partial instead, but it leads to the same problems. For instance, in the login component, we will have to check if the login function existed before we could invoke it, because it's now an optional property and not a required property.
A simple context like this is not useful at scale. If any property inside the context changes, every consumer re-renders. In our example from before, the login component re-renders when we log in, because it is logged in property of the context changes, even though we don't use that property in the login component at all.
A better approach is to use a selectable context. There's a great library called useContextSelector that allows us to do just that. Let's look at the underlying API provider that handles all the primary data for the CRUD application. This API allows us to edit, add, and delete services, and handles updating the state. But we don't want context consumers that aren't using NGATE services to re-render just because we add a new service.
We can now pass a selector function. In the schedule, we select a single property from the API provider, the employees, so we can display them all. But more often, we actually select multiple properties, and to make sure that caching and re-rendering works, we need to shallowy compare the returned objects, so we also pass in a boolean flag. We'll see how this selector function works in a while, but let's first see how the context is created.
We're actually still allowing null here to make creating the context easier. This context has multiple values, so if we had to type them all out, it would be a lot of work. Allowing null makes that a lot easier and a lot more compact. This is because this library.useContextSelector is still required as an initial value for the context. In the provider, we apply the value in the same way as normal, and you can also see here this still allows null to be passed in. But now things get complex. When we select from our context, we have this overloaded function. It allows us to either select the whole context if we pass in nothing, select a single value if we pass in a selector, or select multiple value if we pass in a selector and true. It's not pretty. It's a bit complex, but it works. But if you need this for multiple contexts throughout your application, it's very repetitive to copy around. For that very reason, I've created a small library called Recontextual which does exactly this. It wraps useContextSelector and simplifies the API. It is slightly opinionated about what the API should look like, but I think this is still very generic and can be used for most people. It does not allow a default value, but I also don't think that people use the default value of more complex contexts anyway. Let's see how we use that in ScheduleProvider.
Comments