Using init props
This guide uses the example page setup described on the start page of usage guide
Adding init props to an init action
In our previous example we had a basic init action fetchFooterLinks()
that has no parameters. Often our init actions depend on some value passed through props by the parent component. Props that are used in init actions are called “init props”.
The <Post>
component on our example page needs to initialize by dispatching a fetchPost()
action and passing the correct id of the post. We can set the id
prop of <Post>
as an init prop, and use that to call fetchPost(id)
:
// Post.jsx
export default withInitAction(
['id'],
({ id }, dispatch) => dispatch(fetchPost(id)),
)(
connect(mapStateToProps)(Post)
);
Note that we pass the name of the prop 'id'
as a first argument to withInitAction()
. Otherwise, your init action will not receive this prop.
Just like our basic component, we need to make sure prepareComponent()
is called for <Post>
. Let’s add that to our <PostDetailPage>
init action.
// PostDetailPage.jsx
export default withInitAction(
(props, dispatch) => dispatch(Promise.all([
prepareComponent(Footer),
prepareComponent(Post)
])),
)(PostDetailPage);
However, we are not done yet. react-redux-component-init
now knows that it needs to call the init action that contains fetchPost()
. However, it still has no way of knowing what value to pass for the init prop id. For that reason, it will throw an error:
Component “Post” expected prop “id” but was not passed to prepareComponent
We need to tell prepareComponent()
the values of all init props. In our example, the post id is a URL query parameter that react-router
passes in the props of our top-level component. We need to update the init action of <PostDetailPage>
to pass the id query parameter.
// PostDetailPage.jsx
export default withInitAction(
['location'],
({ location }, dispatch) => dispatch(Promise.all([
prepareComponent(Footer),
prepareComponent(Post, { id: location.query.id })
])),
)(PostDetailPage);
Triggering re-initialize on init prop change
In most cases, you want an init action to dispatch again whenever the init props change value. react-redux-component-init
automatically does this for you! To see how reinitialize works in our example, imagine the following scenario:
- a user clicks on a link to a different post with id
"better-post"
- this triggers a
history.pushState
with update query:?id=better-post
react-router
updates the value ofquery.id
in thelocation
prop<PostDetailPage>
will reinitialize, callingfetchPost("better-post")
- The new data is shown in the
<Post>
component
This works almost perfectly, but we still have a small issue. What if location
updates in some way we don’t care about? For example, say that the user clicks a link to a hash fragment and the hash
property on location changes. We don’t want to initialize the page again! Luckily, we can specify the exact property we want to watch in the array passed to withInitAction()
.
// PostDetailPage.jsx
export default withInitAction(
['location.query.id'], // <== updated init prop
({ location: { query: { id } } }, dispatch) => dispatch(Promise.all([
prepareComponent(Footer),
prepareComponent(Post, { id })
])),
)(PostDetailPage);
Note: the notation { location: { query: { id } } }
is an example of how you can use ES6 parameter destructuring, but it is not required to write your init action using this syntax.
Now <PostDetailPage>
only reinitializes if the location.query.id
property changes. Huzzah!
Disabling re-initialize
It is also possible to indicate that a component should never automatically reinitialize:
// Post.jsx export default withInitAction( ['id'], ({ id }, dispatch) => dispatch(fetchPost(id)), { reinitialize: false } )( connect(mapStateToProps)(Post) );
Next, let’s have a look at <Comments>
inside <Post>
.