Duplicate Data Fetching with Redux & Higher Order Components












0















I'm building a web application with React & Redux + Thunk middleware and am struggling to find the best pattern to connect my components with the data.



I have 3 different presentational components that all rely on the user profile object, which is fetched from the API and stored in Redux state. I have created a higher order function to wrap each of these components, providing them with the user profile object.



This higher order component is connected to Redux and uses selectors to get the data from the Redux store.



const withUser = WrappedComponent => {
class WithUser extends Component {
render = () => {
const {loading, user} = this.props
return <WrappedComponent user={user} loading={loading} {...this.props} />
}
}

const loading = isLoading(['USER_GET'])
const user = getCurrentUser()

return connect(
state => ({ loading: loading(state), user: user(state) }),
{}
)(WithUser)
}

export default withUser


Here is an example of one of my presentational components.



const Profile = props => {
const {name, profileImage} = this.props.user
return (
<Wrapper>
<Avatar src={profileImage} />
<h2>{name}</h2>
</Wrapper>
)
}

export default withUser(Profile)


The crux of my question is: where is the optimal place to fetch the user data? Because I want these components to be entirely independent and context-agnostic, my first thought was to fetch the data using the componentDidMount lifecycle hook in the higher order component:



componentDidMount = () => {
const {getUser, loading, user} = this.props
if (!loading && !user) getUser()
}


The thought behind this is that if there's not already a user in the Redux state, and another component isn't already fetching it, then call getUser(). At first, this seemed to work perfectly, but problems arose when I load a page that initialises multiple components wrapped with withUser() simultaneously. getUser() sent off multiple requests to fetch the user data, before the loading reducer even picked up that the data was fetching, making !isLoading redundant.



Is there a better way to approach this situation? All help is appreciated.



UPDATE: I can get a working solution by fetching data using getUser in the constructor function of the higher order component.



constructor(props) {
super(props)
const { getUser, loading, user } = props
if (!loading && !user) {
getUser()
}
}


My understanding is that fetching data in the constructor is NOT good practice and should be avoided. Thoughts?










share|improve this question





























    0















    I'm building a web application with React & Redux + Thunk middleware and am struggling to find the best pattern to connect my components with the data.



    I have 3 different presentational components that all rely on the user profile object, which is fetched from the API and stored in Redux state. I have created a higher order function to wrap each of these components, providing them with the user profile object.



    This higher order component is connected to Redux and uses selectors to get the data from the Redux store.



    const withUser = WrappedComponent => {
    class WithUser extends Component {
    render = () => {
    const {loading, user} = this.props
    return <WrappedComponent user={user} loading={loading} {...this.props} />
    }
    }

    const loading = isLoading(['USER_GET'])
    const user = getCurrentUser()

    return connect(
    state => ({ loading: loading(state), user: user(state) }),
    {}
    )(WithUser)
    }

    export default withUser


    Here is an example of one of my presentational components.



    const Profile = props => {
    const {name, profileImage} = this.props.user
    return (
    <Wrapper>
    <Avatar src={profileImage} />
    <h2>{name}</h2>
    </Wrapper>
    )
    }

    export default withUser(Profile)


    The crux of my question is: where is the optimal place to fetch the user data? Because I want these components to be entirely independent and context-agnostic, my first thought was to fetch the data using the componentDidMount lifecycle hook in the higher order component:



    componentDidMount = () => {
    const {getUser, loading, user} = this.props
    if (!loading && !user) getUser()
    }


    The thought behind this is that if there's not already a user in the Redux state, and another component isn't already fetching it, then call getUser(). At first, this seemed to work perfectly, but problems arose when I load a page that initialises multiple components wrapped with withUser() simultaneously. getUser() sent off multiple requests to fetch the user data, before the loading reducer even picked up that the data was fetching, making !isLoading redundant.



    Is there a better way to approach this situation? All help is appreciated.



    UPDATE: I can get a working solution by fetching data using getUser in the constructor function of the higher order component.



    constructor(props) {
    super(props)
    const { getUser, loading, user } = props
    if (!loading && !user) {
    getUser()
    }
    }


    My understanding is that fetching data in the constructor is NOT good practice and should be avoided. Thoughts?










    share|improve this question



























      0












      0








      0


      1






      I'm building a web application with React & Redux + Thunk middleware and am struggling to find the best pattern to connect my components with the data.



      I have 3 different presentational components that all rely on the user profile object, which is fetched from the API and stored in Redux state. I have created a higher order function to wrap each of these components, providing them with the user profile object.



      This higher order component is connected to Redux and uses selectors to get the data from the Redux store.



      const withUser = WrappedComponent => {
      class WithUser extends Component {
      render = () => {
      const {loading, user} = this.props
      return <WrappedComponent user={user} loading={loading} {...this.props} />
      }
      }

      const loading = isLoading(['USER_GET'])
      const user = getCurrentUser()

      return connect(
      state => ({ loading: loading(state), user: user(state) }),
      {}
      )(WithUser)
      }

      export default withUser


      Here is an example of one of my presentational components.



      const Profile = props => {
      const {name, profileImage} = this.props.user
      return (
      <Wrapper>
      <Avatar src={profileImage} />
      <h2>{name}</h2>
      </Wrapper>
      )
      }

      export default withUser(Profile)


      The crux of my question is: where is the optimal place to fetch the user data? Because I want these components to be entirely independent and context-agnostic, my first thought was to fetch the data using the componentDidMount lifecycle hook in the higher order component:



      componentDidMount = () => {
      const {getUser, loading, user} = this.props
      if (!loading && !user) getUser()
      }


      The thought behind this is that if there's not already a user in the Redux state, and another component isn't already fetching it, then call getUser(). At first, this seemed to work perfectly, but problems arose when I load a page that initialises multiple components wrapped with withUser() simultaneously. getUser() sent off multiple requests to fetch the user data, before the loading reducer even picked up that the data was fetching, making !isLoading redundant.



      Is there a better way to approach this situation? All help is appreciated.



      UPDATE: I can get a working solution by fetching data using getUser in the constructor function of the higher order component.



      constructor(props) {
      super(props)
      const { getUser, loading, user } = props
      if (!loading && !user) {
      getUser()
      }
      }


      My understanding is that fetching data in the constructor is NOT good practice and should be avoided. Thoughts?










      share|improve this question
















      I'm building a web application with React & Redux + Thunk middleware and am struggling to find the best pattern to connect my components with the data.



      I have 3 different presentational components that all rely on the user profile object, which is fetched from the API and stored in Redux state. I have created a higher order function to wrap each of these components, providing them with the user profile object.



      This higher order component is connected to Redux and uses selectors to get the data from the Redux store.



      const withUser = WrappedComponent => {
      class WithUser extends Component {
      render = () => {
      const {loading, user} = this.props
      return <WrappedComponent user={user} loading={loading} {...this.props} />
      }
      }

      const loading = isLoading(['USER_GET'])
      const user = getCurrentUser()

      return connect(
      state => ({ loading: loading(state), user: user(state) }),
      {}
      )(WithUser)
      }

      export default withUser


      Here is an example of one of my presentational components.



      const Profile = props => {
      const {name, profileImage} = this.props.user
      return (
      <Wrapper>
      <Avatar src={profileImage} />
      <h2>{name}</h2>
      </Wrapper>
      )
      }

      export default withUser(Profile)


      The crux of my question is: where is the optimal place to fetch the user data? Because I want these components to be entirely independent and context-agnostic, my first thought was to fetch the data using the componentDidMount lifecycle hook in the higher order component:



      componentDidMount = () => {
      const {getUser, loading, user} = this.props
      if (!loading && !user) getUser()
      }


      The thought behind this is that if there's not already a user in the Redux state, and another component isn't already fetching it, then call getUser(). At first, this seemed to work perfectly, but problems arose when I load a page that initialises multiple components wrapped with withUser() simultaneously. getUser() sent off multiple requests to fetch the user data, before the loading reducer even picked up that the data was fetching, making !isLoading redundant.



      Is there a better way to approach this situation? All help is appreciated.



      UPDATE: I can get a working solution by fetching data using getUser in the constructor function of the higher order component.



      constructor(props) {
      super(props)
      const { getUser, loading, user } = props
      if (!loading && !user) {
      getUser()
      }
      }


      My understanding is that fetching data in the constructor is NOT good practice and should be avoided. Thoughts?







      reactjs asynchronous design-patterns redux react-redux






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 21 '18 at 0:05







      bh94

















      asked Nov 20 '18 at 7:03









      bh94bh94

      13




      13
























          1 Answer
          1






          active

          oldest

          votes


















          0














          I think it's problem more redux storing than a HOC.A little advise here, store the user data in redux with Array or JSON.Every time fetch a new user data, push it into Array or JSON.



          newState.user = [...newState,response.data] //Array
          newState.user = { //JSON
          ...newState,
          'profileData': response.data
          }


          And instead of !loading,use this:



          if (!props.profileData) getUser()





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53387823%2fduplicate-data-fetching-with-redux-higher-order-components%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            I think it's problem more redux storing than a HOC.A little advise here, store the user data in redux with Array or JSON.Every time fetch a new user data, push it into Array or JSON.



            newState.user = [...newState,response.data] //Array
            newState.user = { //JSON
            ...newState,
            'profileData': response.data
            }


            And instead of !loading,use this:



            if (!props.profileData) getUser()





            share|improve this answer




























              0














              I think it's problem more redux storing than a HOC.A little advise here, store the user data in redux with Array or JSON.Every time fetch a new user data, push it into Array or JSON.



              newState.user = [...newState,response.data] //Array
              newState.user = { //JSON
              ...newState,
              'profileData': response.data
              }


              And instead of !loading,use this:



              if (!props.profileData) getUser()





              share|improve this answer


























                0












                0








                0







                I think it's problem more redux storing than a HOC.A little advise here, store the user data in redux with Array or JSON.Every time fetch a new user data, push it into Array or JSON.



                newState.user = [...newState,response.data] //Array
                newState.user = { //JSON
                ...newState,
                'profileData': response.data
                }


                And instead of !loading,use this:



                if (!props.profileData) getUser()





                share|improve this answer













                I think it's problem more redux storing than a HOC.A little advise here, store the user data in redux with Array or JSON.Every time fetch a new user data, push it into Array or JSON.



                newState.user = [...newState,response.data] //Array
                newState.user = { //JSON
                ...newState,
                'profileData': response.data
                }


                And instead of !loading,use this:



                if (!props.profileData) getUser()






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 20 '18 at 9:45









                RootRoot

                1,503128




                1,503128






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53387823%2fduplicate-data-fetching-with-redux-higher-order-components%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    MongoDB - Not Authorized To Execute Command

                    in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith

                    How to fix TextFormField cause rebuild widget in Flutter