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})}
/>
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 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...
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 trackpredicate
(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 })}>