Best practice to convert Recoil atomFamilies and selectorFamilies to atoms #159
-
|
We are currently converting our form state from Recoil to Zedux. Our app has a lot of forms so we use the selectorFamily and atomFamily functionality from Recoil to keep the state within a form isolated from other forms. We use React context on our form to hold a unique identifier string for the form called Here is a really simple example of our Recoil code: (not our real code, but a simple example to for this discussion) /**
* Atom family to store form field values
*/
export const formFieldValuesAtom = atomFamily<FieldValues, string>({
key: 'formFieldValuesAtomState',
default: {},
});
const ComponentInForm = () => {
// Get the form context
const form = useContext(FormContext);
const [fieldValues, setFieldValues] = useRecoilState(formFieldValuesAtom(form?.stateId ?? ''));
}As you can see in our We started to set up this code with Zedux: export const formFieldValuesAtom = atom('formButtonClickedAtom', (formId: string) => {
const store = injectStore({});
return store;
});
const ComponentInForm = () => {
// Get the form context
const form = useContext(FormContext);
const [fieldValues, setFieldValues] = useAtomState(formFieldValuesAtom, [form?.stateId ?? '']);
}That technically seems to work. However, I'm not sure if it's best-practice. What is the best way to emulate the atomFamily of Recoil in Zedux when the family key isn't used in the atom? eslint is rightly complaining about the unused (I saw the example on https://omnistac.github.io/zedux/docs/about/recoil-comparison but it didn't match our use case.) Thank you for any guidance. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
|
Atom params are indeed the Zedux equivalent of Recoil's There are a few approaches Underscore-PrefixWe do exactly what you have in some places. We have the export const formAtom = atom('form', (_formId: string) => ...);Use the ParamIf you don't want that eslint rule, one thing I do in some side projects is export the id parameter: export const formAtom = atom('form', (formId: string) => {
const store = injectStore({});
return api(store).setExports({ formId })
});This can actually be nice for debugging. Atom FactoriesIf you really want to be rid of the extra parameter, you can use an atom factory to create atom templates with the formId as part of the atom key string - that's all the param is doing anyway. The factory would return a singleton atom that's instantiated once in the parent component and provided via normal React Context. Full example: const formAtomFactory = (formId: string) =>
atom(`form-${formId}`, () => {
const store = injectStore({});
return store;
});
type FormAtomType = ReturnType<typeof formAtomFactory>;
const formAtomContext = createContext<AtomInstanceType<FormAtomType> | undefined>(undefined)
const useFormAtomContext = () => {
const formAtomInstance = useContext(formAtomContext)
if (!formAtomInstance) {
throw new Error('Form atom was not provided')
}
return formAtomInstance
}
function ParentComponent() {
const atomTemplate = useMemo(() => formAtomFactory('my-form-id'), [])
const formAtomInstance = useAtomInstance(atomTemplate);
return (
<formAtomContext.Provider value={formAtomInstance}>
<ChildComponent />
</formAtomContext.Provider>
)
}
function ChildComponent() {
const formAtomInstance = useFormAtomContext()
const [fieldValues, setFieldValues] = useAtomState(formAtomInstance)
...
} |
Beta Was this translation helpful? Give feedback.
-
|
Thank you @bowheart! The second option with the export is probably what we'll do. One more question. Our Form component wraps all the fields. When the Form is unmounted we were using the recoil Do we need to do that type of cleanup with Zedux? Or does Zedux clean itself up well enough? Our app has a lot of forms and each form will have a separate id, and therefore add to the state. This is some simple code on how we did the cleanup before. const FormComponent = () => {
const resetFormState = useResetRecoilState(formAtom(props.stateId));
const resetFieldInitialValues = useResetRecoilState(formFieldInitialValuesAtom(props.stateId));
const resetFieldPersistentValues = useResetRecoilState(formFieldValuesPersistentAtom(props.stateId));
const resetFieldValidations = useResetRecoilState(formFieldValidationAtom(props.stateId));
const resetFieldValues = useResetRecoilState(formFieldValuesAtom(props.stateId));
const resetFormLeaveAfterSubmit = useResetRecoilState(formLeaveAfterSubmitAtom(props.stateId));
const resetRepeatingItemIds = useResetRecoilState(formRepeatingItemIdsAtom(props.stateId));
// Reset form state when component unmounts
useEffect(
() => () => {
resetFormState();
resetFieldInitialValues();
resetFieldValidations();
resetFieldValues();
resetFieldPersistentValues();
resetFormLeaveAfterSubmit();
resetRepeatingItemIds();
},
[
resetFormState,
resetFieldInitialValues,
resetFieldValidations,
resetFieldValues,
resetFormLeaveAfterSubmit,
resetRepeatingItemIds,
resetFieldPersistentValues,
],
);
} |
Beta Was this translation helpful? Give feedback.
Atom params are indeed the Zedux equivalent of Recoil's
atomFamily(andselectorFamilytoo, really, since all Zedux atoms can also be "selectors" by injecting any other atom).There are a few approaches
Underscore-Prefix
We do exactly what you have in some places. We have the
no-unused-varseslint rule configured with"argsIgnorePattern": "^_"which makes eslint ignore underscore-prefixed varsUse the Param
If you don't want that eslint rule, one thing I do in some side projects is export the id parameter: