useSettingsLoader
Documentation: useSettingsLoader
Hook
The useSettingsLoader
hook is a utility for fetching and updating organizational and resource-level configurations from an Apollo Client query in a Remix application. It provides an easy way to manage configurations with the ability to update them and automatically refetch them when needed.
Hook Signature
export const useSettingsLoader = <K = IConfigurationsFlattenedKeys, R = IPreferences>(
settingsVariable: ISettingsLoaderVariable<K>,
): IResponse<K>
Parameters
settingsVariable: ISettingsLoaderVariable<K>
:- configKey: The key used to identify the configuration to fetch.
- skip (optional): A flag to skip querying or fetching the configuration when set to
true
.
Return Value
The hook returns an object of type IResponse<K>
with the following properties:
- data: The current configuration value (
string | number | boolean
) corresponding to theconfigKey
. It could come from either the initial data provided by the Remix loader or the refetched data from Apollo Client. - loading: A
boolean
indicating whether the hook is in a loading state. - error: Any error that occurred during the fetching or updating of configurations.
- preferencesInput: Input preferences related to the configuration, retrieved from the Remix loader's context.
- updateConfiguration: A function that allows updating the configuration value for a given key. After successfully updating, it refetches the configuration from the server.
Prerequisites
- Apollo Client Setup: Your project should be configured with Apollo Client to handle GraphQL queries and mutations.
- Route Configuration: In
compute.ts
of the specific route should haveconfigurations
with optionalresourceParams
. Please check the docs under the remix section for more information.
useSettingsLoader
Hook Usage Guide
The useSettingsLoader
hook is a utility for managing configuration settings in a React application, particularly in scenarios where initial configurations are loaded through a data loader (like Remix's useLoaderData
), and you want to handle updates or refetches using Apollo Client.
Here’s a step-by-step guide on how to use the useSettingsLoader
hook effectively in your application.
Prerequisites
- Apollo Client Setup: Your project should be configured with Apollo Client to handle GraphQL queries and mutations.
- Remix Data Loader: You'll use Remix's
useLoaderData
to fetch the initial configurations from the server during route rendering. - React Environment: Ensure that your application is wrapped in an
ApolloProvider
andMemoryRouter
(for testing), or another appropriate router setup during runtime.
Basic Usage
1. Loading Initial Configuration
The useSettingsLoader
hook is designed to first retrieve configuration data from Remix’s useLoaderData
. You need to pass a configKey
to identify which configuration value you are targeting.
Here’s how you can use it in a basic component:
import { useSettingsLoader } from './hooks/useSettingsLoader';
const NotificationSettings = () => {
const { data, loading, error } = useSettingsLoader({
configKey: 'account.notification.primaryEmail',
});
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return <div>Notification Email: {data}</div>;
};
export default NotificationSettings;
configKey
: This is the key used to fetch a specific configuration. In the above example, we are retrieving theaccount.notification.primaryEmail
value.data
: The configuration value returned by the hook. It will either come from the Remix loader (initially) or be refetched via Apollo Client.loading
: A boolean indicating whether the configuration is currently being fetched.error
: If any errors occur during the data fetching process, they will be captured here.
2. Updating the Configuration
The useSettingsLoader
hook also provides an updateConfiguration
function, which you can use to update the configuration and trigger a refetch of the data.
Here’s an example:
const NotificationSettings = () => {
const { data, updateConfiguration } = useSettingsLoader({
configKey: 'account.notification.primaryEmail',
});
const handleUpdate = async () => {
await updateConfiguration({
updateKey: 'account.notification.primaryEmail',
value: 'new-email@example.com',
});
};
return (
<div>
<p>Notification Email: {data}</p>
<button onClick={handleUpdate}>Update Email</button>
</div>
);
};
updateConfiguration
: This function accepts an object with parameters:updateKey
: The key for the configuration to update (optional if it's the same as theconfigKey
).value
: The new value to set for the configuration.updateOverrides
: Any additional overrides for the update.target
: Where the configuration update is targeted (e.g., account, organization, etc.).
Once the update is successful, the hook will refetch the configuration data, and the data
will reflect the updated value.
3. Skipping Apollo Queries
In some scenarios, you might want to skip the Apollo query altogether (e.g., if the configuration data is already up-to-date or doesn’t need fetching at that moment). You can use the skip
parameter in the hook’s input.
const NotificationSettings = () => {
const { data } = useSettingsLoader({
configKey: 'account.notification.primaryEmail',
skip: true,
});
return <div>{data ? `Notification Email: ${data}` : 'No email set'}</div>;
};
Error Handling
Always ensure you handle the error
state returned by the hook. Errors could occur due to network issues, GraphQL errors, or even loader issues.
const NotificationSettings = () => {
const { data, loading, error } = useSettingsLoader({
configKey: 'account.notification.primaryEmail',
});
if (loading) {
return <div>Loading settings...</div>;
}
if (error) {
return <div>Error loading settings: {error.message}</div>;
}
return <div>{`Notification Email: ${data}`}</div>;
};
Testing Components Using useSettingsLoader
When writing tests for components that use useSettingsLoader
, you will need to mock both useLoaderData
(for initial data) and the Apollo Client (for updates or refetches). Additionally, you should wrap your test in a MemoryRouter
and ApolloProvider
.
Here’s a basic test setup:
import { render, screen, waitFor, act } from '@testing-library/react';
import { createRemixStub, json } from '@remix-run/testing'; // Remix testing utilities
import MyComponent from './MyComponent'; // The component using useSettingsLoader
import { DefaultConfiguration } from './fixtures/default-configuration';
const preferencesInput = {
editableSettingsInput: 'some-input',
};
describe('useSettingsLoader Hook Test with RemixStub', () => {
it('should load initial configuration from loader', async () => {
// Create a RemixStub for the test
const RemixStub = createRemixStub([
{
path: '/',
Component: MyComponent,
loader() {
return json({
configurations: DefaultConfiguration.contents,
dataContext: { preferencesInput: preferencesInput },
});
},
},
]);
// Render the component within the RemixStub
await act(async () => {
render(<RemixStub />);
});
// Wait for the component to load and check the configuration output
await waitFor(() => {
expect(screen.getByTestId('config-output').textContent).toContain(
JSON.stringify(DefaultConfiguration.contents.organization.teams.visibility)
);
});
// Simulate updating the configuration
const updateButton = screen.getByRole('button', { name: /Update Email/i });
await act(async () => {
updateButton.click();
});
// Check if the new value is rendered after the update
await waitFor(() => {
expect(screen.getByTestId('config-output').textContent).toContain('new-email@example.com');
});
});
});
Best Practices
Efficient Query Usage: Use the
skip
option when you don’t need to fetch the configuration data, such as in scenarios where the data is already available or doesn't need to be refetched.Optimize for Performance: Ensure you leverage the
cache-first
policy of Apollo Client to avoid unnecessary network requests. Use theskip
parameter wisely when refetching is unnecessary.Error Handling: Always handle errors gracefully. Whether during the initial fetch or a refetch, providing fallback UI for error states improves the user experience.
Conclusion
The useSettingsLoader
hook offers an efficient way to manage configuration settings in your React applications, particularly when using Remix and Apollo Client. It handles both initial data loading via useLoaderData
and updates via Apollo Client’s GraphQL queries. Whether you're fetching configurations initially or updating them, useSettingsLoader
provides the flexibility and scalability you need to manage configuration settings efficiently in any React environment.