jupiteror
jupiteror

Reputation: 1205

How to use react-final-from calculated fields with an array of objects

I have an array of objects in react-final-form with a sum field. At the end I would like to count the total of all sums. So I'm using calculated fields from final-form-calculate like this:

const calculator = createDecorator({
  field: /day\[\d\]\.sum/, // when a field matching this pattern changes...
  updates: (value, name, allValues) => {
    console.log("Updated field", value, name);
    // ...update the total to the result of this function
    total: (ignoredValue, allValues) =>
      (allValues.day || []).reduce((sum, value) => sum + Number(value || 0), 0);
    return {};
  }
});

When I enter values in the inputs, console.log is called, but the total is not updated. I guess it doesn't pick the values from the necessary fields. How can I fix it? Here is my codesandbox https://codesandbox.io/s/react-final-form-calculated-fields-hkd65?fontsize=14.

Upvotes: 0

Views: 1358

Answers (2)

Istvan Szasz
Istvan Szasz

Reputation: 1567

Final-form-calculate docs, say the the updater can be:

Either an object of updater functions or a function that generates updates for multiple fields.

In your example, the code was some kind of mix between them. Also value.sum contains the entered numbers, not value.

Here's how to do it correctly, with an object of updater functions:

const calculator = createDecorator({
    field: /day\[\d\]\.sum/,
    updates: {
        total: (ignoredValue, allValues) => (allValues.day || []).reduce((sum, value) => sum + Number(value.sum || 0), 0)
    }
});

or updates for multiple fields (just one actually, but could be more):

const calculator = createDecorator({
    field: /day\[\d\]\.sum/,
    updates: (ignoredValue, fieldName, allValues) => {
        const total = (allValues.day || []).reduce((sum, value) => sum + Number(value.sum || 0), 0);
        return { total };
    }
});

Also, here are the updater Typescript definitions, for reference:

export type UpdatesByName = {
  [FieldName: string]: (value: any, allValues?: Object, prevValues?: Object) => any
}

export type UpdatesForAll = (
  value: any,
  field: string,
  allValues?: Object,
  prevValues?: Object,
) => { [FieldName: string]: any }

export type Updates = UpdatesByName | UpdatesForAll

Upvotes: 1

Tunmise Ogunniyi
Tunmise Ogunniyi

Reputation: 2573

You have some syntactical error in your code snippet, specifically the calculator function. This version of the function works:

const calculator = createDecorator({
  field: /day\[\d\]\.sum/, // when a field matching this pattern changes...
  updates:  {
    // ...update the total to the result of this function
    total: (ignoredValue, allValues) => (allValues.day || []).reduce((sum, value) => sum + Number(value.sum || 0), 0),
  }
});

There are two major changes I did,

  • In the reduce call back, I changed Number(value || 0) to Number(value.sum || 0)
  • I also set the updates property to an object instead of a function.

Upvotes: 1

Related Questions