How to use useComputed inside useMemo hook?

During development Eslint prompted me with this information:

The ‘fields’ object makes the dependencies of useCallback Hook (at line 183) change on every render. Move it inside the useCallback callback. Alternatively, wrap the initialization of ‘fields’ in its own useMemo() Hook

Code looks like this:

const fields: { [p: string]: EntityField } = {
        'Kredieten': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.kredieten.id), [metadataService]),
        'Deelnemingen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.deelnemingen.id), [metadataService]),
        'Toeleveranciers': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.toeleveranciers.id), [metadataService]),
        'Vervoersmiddelen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.vervoersmiddelen.id), [metadataService]),
        'Liquide middelen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.liquideMiddelen.id), [metadataService]),
        'Voorraden': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.voorraden.id), [metadataService]),
        'Groepsvermogen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.groepsvermogen.id), [metadataService]),
        'Gebouwen en terreinen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.gebouwenEnTerreinen.id), [metadataService]),
        'Machines & Installaties': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.machinesAndInstallaties.id), [metadataService]),
        'Inventaris': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.inventaris.id), [metadataService]),
        'Effecten': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.effecten.id), [metadataService]),
        'Rechten & Vergunningen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.rechtenAndVergunningen.id), [metadataService]),
    };

And the handle save:

const handleSave = useCallback(async () =>
    {
        setLoading(true);

        Object.keys(data).forEach((x: string) => {
            mutationHandler.updateEntityWithProperty(
                dossier.entity,
                new FieldProperty(fields[x]),
                DataObjectValue.create(
                    new DataObject(
                        fields[x].specification,
                        {boolean: data[x]},
                    )
                ),
            );
        })

        try
        {
            await mutationHandler.commit();

            setLoading(false);
            enqueueSnackbar('Succesvol opgeslagen');
        }
        catch (e)
        {
            showError(e, 'Opslaan mislukt');

            setLoading(false);
        }
    }, [data, mutationHandler, dossier.entity, fields, enqueueSnackbar, showError]);

What did I miss, that whenever I want to wrap the fields in a useMemo:

const fields: { [p: string]: EntityField } = useMemo(() => ({

Eslint is screaming about useComputed?

Forgot to mention, this code is partly responsible for check boxes.

>Solution :

React hooks can’t be called from nested functions, i.e. the useCallback or useMemo callback functions.

Make all the useComputed calls on their own first, then memoize the fields object.

Example:

const Kredieten = useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.kredieten.id), [metadataService]);
const Deelnemingen = useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.deelnemingen.id), [metadataService]);
...
...
const RechtenVergunningen = useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.rechtenAndVergunningen.id), [metadataService]);

...

const fields: { [p: string]: EntityField } = useMemo(() => ({
  Kredieten,
  Deelnemingen,
  ...
  ...
  'Rechten & Vergunningen': RechtenVergunningen,
}), [Kredieten, Deelnemingen, ..., RechtenVergunningen]);

Leave a Reply