Link Search Menu Expand Document

Expected component "{Component}" to be prepared but prepareComponent has not been called

Explanation

Possible causes

Your component is a top-level (page) component

If your component is a top-level component and you’re getting this error, there may be an error in the setup of this library. Please verify you have completed the steps in setup instructions. Specifically, make sure you call prepareComponents for your top-level components in the server-side page rendering function.

prepareComponent() not called at all

Please make sure you have called prepareComponent for your component…

  • from the init action of the current top-level (page) component
    or
  • from the init action of another parent component that has been prepared

If you’re unsure about how this works, please read basic usage in the usage guide.

prepareComponent() is called but the action is not dispatched

You may have forgotton to pass the prepareComponent action to redux dispatch:

export default withInitAction(
  (props, dispatch) => prepareComponent(MyComponent), // <== forgot to dispatch!
  // fix: (props, dispatch) => dispatch(prepareComponent(MyComponent))
)(MyPage);

prepareComponent() is called with the unwrapped (inner) component

You may have exported the unwrapped version of your component. For example:

// MyComponent.jsx
export class MyComponent extends React.Component {
  // ...
}

export default withInitAction(
  // ...
)(MyComponent);
// another module
import { MyComponent } from './components/MyComponent'; // <== accidentally imported the unwrapped component!
// ...
dispatch(prepareComponent(MyComponent));

In the above example, the default export should be imported instead of the named export:

// fixed
import MyComponent from './components/MyComponent';

prepareComponent() is called from a clientOnly init action

clientOnly init actions are not called on the server. If this is where you dispatch prepareComponent, it will not happen in time. For example:

// <Post> Component
export default withInitAction(
  ['postId', 'authorId'],
  {
    clientOnly: ({ postId, authorId }, dispatch) => Promise.all([
      dispatch(fetchPostComments(postId)),
      dispatch(prepareComponent(UserAvatar, { userId: authorId })),
    ])
  }
)(Post);

// <UserAvatar> Component (rendered inside <Post>)
export default withInitAction(
  ['userId'],
  ({ userId }, dispatch) => dispatch(prepareUserAvatar(userId))
)(UserAvatar);

There are 3 solutions possible for this:

  1. split your parent init action between prepared and clientOnly
    // Change <Post> to:
    export default withInitAction(
      ['postId', 'authorId'],
      {
        prepared: ({ postId, authorId }, dispatch) => dispatch(prepareComponent(UserAvatar, { userId: authorId })),
        clientOnly: ({ postId, authorId }, dispatch) => dispatch(fetchPostComments(postId))
      }
    )(Post);
    
  2. make the entire parent action prepared
    // Change <Post> to:
    export default withInitAction(
      ['postId', 'authorId'],
      ({ postId, authorId }, dispatch) => Promise.all([
        dispatch(fetchPostComments(postId)),
        dispatch(prepareComponent(UserAvatar, { userId: authorId })),
      ])
    )(Post);
    
  3. change all child components of clientOnly actions to also be clientOnly
    // Change <UserAvatar> to:
    export default withInitAction(
      ['userId'],
      { clientOnly: ({ userId }, dispatch) => dispatch(prepareUserAvatar(userId)) }
    )(UserAvatar);
    // Change <Post> to:
    export default withInitAction(
      ['postId', 'authorId'],
      {
        clientOnly: ({ postId, authorId }, dispatch) => dispatch(fetchPostComments(postId)),
        // prepareComponent no longer necessary for clientOnly UserAvatar
      }
    )(Post);
    

More details