The Decimator
The Decimator

Reputation: 71

Understanding HOCs with Redux

I have a users component that just displays a list of users. I have tried to wrap it in a HOC loading component so that it only displays once the users are loaded, otherwise shows a loading spinner (well just text for now)

this is my HOC:

const Loading = (propName) => (WrappedComponent) => {
  return class extends React.Component{
    render(){
      return this.props[propName].length === 0 ? <div> Loading... </div> : <WrappedComponent {...this.props} />
    }
  }
}

export default Loading;

at the bottom of my users component I have this:

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Loading('users')(Users));

currently, the word Loading... is just staying on screen. and propName is coming through as undefined

I think for some reason the users component is never getting populated. what have i done wrong?

Upvotes: 4

Views: 102

Answers (1)

devserkan
devserkan

Reputation: 17618

Update after comments

My answer below is a misleading one since I hadn't understood your intention properly at that time. Also, my explanation about not getting props is somehow wrong. It is true if we don't render the components but here you are doing it. So, the problem was not that.

The problem here is your Loading component isn't rendered again after fetching users. Actually, you never fetch the users :) Here are the steps of your app (probably).

  • You are exporting a HOC function, not the Wrapped one here. It comes from your Users file but it does not export the real Users component. This is important.
  • Your parent renders the first time and it renders the exported HOC component.
  • Your child component renders and fall into Loading one not the Users one.
  • In Loading your users prop is empty, so you see Loading....
  • Your Users component never renders again. So, fetching the users there don't update the state.

Your solution is extracting the fetch out of Users and feed this component. Probably in a parent one. So:

  • Parent fetches the users then renders itself and all its children.
  • Your Loading HOC component renders a second time.

I don't know how do you plan to use this HOC but if I understood right (since I'm not so experienced with HOC) in your case the problem is you are not passing any prop to the Loading function. This is because you are not using it as a regular component here. It is a function and propName here is just an argument.

When we render a stateless function like this:

<Loading propName="foo" />

then there will be a props argument for our function. If we don't render it like that there will be no props argument and no props.propName. If this is wrong please somebody fix this and explain the right logic. So, you want to do something like this probably:

class App extends React.Component {
  render() {
    return (
      <div><FooWithLoading /></div>
    );
  }
}

const Loading = (users) => (WrappedComponent) => {
  return class extends React.Component {
    render() {
      return (
        users.length === 0 ? <div> Loading... </div> :
        <WrappedComponent
          users={users}
        />
      );
    }
  }
};

const Foo = props => {
return (
  <div>
    Users: {props.users}
  </div>
);
}

const FooWithLoading = Loading("foobar")(Foo);

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

So in your case:

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Loading('users')(Users));

should work?

Or you need to render your component properly in a suitable place of your app.

Upvotes: 2

Related Questions