Ref
Ref (short for reference) is a mutable object that allows you save value without any reactivity. This allows you to share data across multiple runs of an effects.
Usage
import { ref } from '@nalanda/core';
const getValueRef = ref(initialValue);
effect((store) => {
const valueRef = getValueRef(store);
valueRef.current = 'newValue'; // to set the value
});
Sharing ref between effects
Here is an example where a shared ref is used to manage aborting of a request across multiple effects.
import { ref } from '@nalanda/core';
const key = createKey('mySlice', []);
const getAbortRef = ref(new AbortController());
key.effect(async (store) => {
const abortRef = getAbortRef(store);
if (!abortRef.current.signal.aborted) {
const data = await fetchUserData();
// handle fetched data
// reset the abort controller
abortRef.current = new AbortController();
}
});
// an effect that can cancel the fetch
key.effect(async (store) => {
const abortRef = getAbortRef(store);
const { isUserLoggedIn } = useSlice.track(store);
// if user logs out, cancel any ongoing fetch
if (!isUserLoggedIn) {
abortRef.current.abort();
}
});
Typescript
You pass a type argument to ref
to specify the type of the value you want to store.
const getUsernameRef = ref<string | undefined>(undefined);
key.effect(async (store) => {
const usernameRef = getUsernameRef(store);
// fetch username if not set
if (!usernameRef.current) {
const username = await fetchUsername();
usernameRef.current = username;
}
});
When to use them?
Refs share a lot of similarities with state. However, they are not the same. By default its a good idea to use slice state whever possible.
-
When you don't need reactivity. Setting a ref value will not trigger a re-render. This is useful when you want to store a value that is not related to the UI.
-
When you need to share imperative data across multiple effects. Things like
AbortController
,setTimeout
andsetInterval
are good examples of things you want to share across multiple effects without the need to re-render. -
When the data is local to the effect. If you need to store a value that is local to the effect and does not need to be widenly available across your application. For example, storing a
setInterval
id to clear it later.
Best Practices
- Define refs at the module level only
import { ref } from '@nalanda/core';
key.effect(() => {
// ❌ Don't declare ref inside effect
const getCounterRef = ref(initialValue);
})
// ✅ Do this - declare ref at the module level
const getCounterRef = ref(initialValue);
key.effect((store) => {
const counterRef = getCounterRef(store);
});