-
-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Value equality in selectors? #38
Comments
Looks like this isn't currently possible. But following a similar discussion in the main Recoil repo, I've rewritten the code that they provide as a solution in Rescript. Posting it here, in case somebody may find this helpful: type equalAtomOptions<'value> = {
key: string,
default: 'value,
equals: ('value, 'value) => bool,
}
/**
* Use a writable selector to prevent excess renders.
* If the setting value is equal to the current value, don't change anything.
*/
let equalAtom = (options: equalAtomOptions<'value>): Recoil.readWrite<'value> => {
let {key, equals, default} = options
let inner = Recoil.atom({
key: `${options.key}_inner`,
default: default,
})
Recoil.selectorWithWrite({
key: key,
get: ({get}) => get(inner),
set: ({get, set}, newValue) => {
let current = get(inner)
if !equals(newValue, current) {
set(inner, _ => newValue)
}
},
})
}
type equalSelectorOptions<'value> = {
key: string,
get: Recoil.getValue<'value>,
equals: ('value, 'value) => bool,
}
/**
* Use a wrapper selector to prevent excess renders.
* If the latest selection is value-equal to prior ref, return the prior ref.
*/
let equalSelector = (options: equalSelectorOptions<'value>): Recoil.readOnly<'value> => {
let inner = Recoil.selector({
key: `${options.key}_inner`,
get: options.get,
})
let prior = ref(None)
Recoil.selector({
key: options.key,
get: ({get}) => {
let latest = get(inner)
switch prior.contents {
| Some(prior) if options.equals(latest, prior) => prior
| _ =>
prior := Some(latest)
latest
}
},
})
}
type equalSelectorFamilyOptions<'parameter, 'value, 'paramIdentity> = {
key: string,
get: 'parameter => Recoil.fn<Recoil.getValue<'value>>,
equals: ('value, 'value) => bool,
emptyParamMap: Belt.Map.t<'parameter, 'value, 'paramIdentity>,
}
/**
* Use a wrapper selector to prevent excess renders.
* If the latest selection is value-equal to prior ref, return the prior ref.
*/
let equalSelectorFamily = (
options: equalSelectorFamilyOptions<'parameter, 'value, 'paramIdentity>,
) => {
let inner_key = `${options.key}_inner`
let inner = Recoil.selectorFamily({
key: inner_key,
get: options.get,
})
let priors = ref(options.emptyParamMap)
Recoil.selectorFamily({
key: options.key,
get: (param: 'parameter) => Fn(
({get}) => {
let innerValue = inner(param)
let latest = get(innerValue)
let prior = Belt.Map.get(priors.contents, param)
switch prior {
| Some(prior) if options.equals(latest, prior) =>
prior
| _ =>
priors := Belt.Map.set(priors.contents, param, latest)
latest
}
},
),
})
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, it seems that selectors will always cause a rerender if the reference changed (even though the value stays the same). Is there a way to use value equality instead?
Thanks!
The text was updated successfully, but these errors were encountered: