Tracking Collections

React Redux Form provides the track() function, which returns a function that, given state, returns the expected model string:

import { track, Control } from 'react-redux-form';

// in render...
// this will represent the friend in the user.friends[]
// collection whose id === 123.
<Control.text
  model={track('user.friends[].email', {id: 123})}
/>

Explanation

Let's say you have a collection of data in a model that looks like this:

const goats = [
  { id: 101, name: 'Australian Dwarf Goat' },
  { id: 202, name: 'Booted Goat' },
  { id: 303, name: 'Pygmy Goat' }
];

and you want to do any of the following:

  • Insert an item into the collection at any index
  • Delete an item from the collection
  • Sort the collection
  • Shuffle the items in the collection
  • ...etc.

A naïve approach to creating fields for each item is by index:

// in connected component's render()
const { goats } = this.props;

return (
  <div>
  { goats.map((goat, index) =>
    <Control.text model={`goats[${index}].name`} key={index}>
  }
  </div>
);

However, if we do any of the above actions that might change the index of an item, despite that item remaining the same, you will get unexpected results.

For example, if goats[2].name === 'Pygmy Goat' and we dispatch(actions.remove('goats', 1)), then goats[2].name === 'Booted Goat', which is not a desirable result.

So what do we do?

Model Getters

Model getter functions answer the question, "What is the path to this entity?" especially if the path may change over time. It's a function that:

  • takes one argument: state (Object), which is the entire state tree of the store
  • and returns: model (String) - the string representation of the path to the model.

Here is an example model function that will always return the path to the Pygmy Goat (with an ID of 303):

function pygmyGoatNameModel(state) {
  const goatIndex = state.goats.findIndex((goat) => {
    // hardcoded index; let's fix this
    return goat.id === 303;
  });

  return `goats[${goatIndex}].name`;
}

And you can use this model getter in any component that takes a model={...} prop:

<Control.text model={pygmyGoatNameModel}>

That way, the model will always point to the correct entity (goat) even if the array indexes change.

However, this is a bit verbose. There's a simpler way of creating model getters that track model paths in collections...

The track() Function

You can create model getters using the track() function, which takes two arguments:

  • model (String) - the model that represents the collection and property you want to track
  • predicate (Function | Any) - a function or Lodash iteratee that finds the correct model to track.

For example, I can track the pygmy goat name by ID like so:

// With a predicate function
<Control.text model={track('goats[].name', (goat) => goat.id === 303)}>

// With a Lodash iteratee (shortand)
<Control.text model={track('goats[].name', { id: 303 })}>

results matching ""

    No results matching ""