Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get rid of withAuth? #33

Open
lorensr opened this issue Jun 11, 2018 · 5 comments
Open

Get rid of withAuth? #33

lorensr opened this issue Jun 11, 2018 · 5 comments

Comments

@lorensr
Copy link
Member

lorensr commented Jun 11, 2018

Made withAuth to not have to mess with <App> and give example of making HOC. Not feeling like either reason is very important.

Globals?

Inspired by DHH's series on writing software well, in an episode recommending judicious use of globals, eg for the current user.

Could be used for login/logout functions.

  • Pro: Easy
  • Con: Testing—for various forms of testing (including explicit unit tests and things like storybook), you then have to mock out the globals.
// CurrentUserQuery.js
export let login, logout

class CurrentUserQuery extends Component
  constructor() {
    login = this.login
    logout = this.logout      
  }

  login = () => 
  logout = () => 

So that eg in Profile.js you can:

import { login, logout } from './CurrentUserQuery'

instead of getting them as props.

Prop drilling vs HOC vs Context/unstated

Need a reactive data source for loading & currentUser, so can't do globals.

Reusable query

Could do a withUser() HOC that provides them through graphql() to any component that needs it. All using default cache-first, and deduplication should combine them into one query, while also maintaining data.loading/loadingUser.

  • Pro: concise. Using Apollo store like the global variable it is
  • Con: how to incorporate loggingIn into loading? In the single-use withAuth, it's defined as: loading={loading || this.state.loggingIn}
class Profile extends Component {
  ...
}

export default withUser(Profile)

(or alternatively could make a reusable <WithUser> query component, but that takes more characters and indentation to use)

Prop drilling

A single normal query at the top. Lot of drilling. It's nice for testing, but not nice to read/write. Would personally prefer to avoid.

<CurrentUserQuery>
  {({ loading, error, data }) => (
    <App loadingUser={loading} currentUser={data.currentUser} />
  )}
</CurrentUserQuery>

Context/unstated

  • Pro: no drilling
  • Con: verbose
@unicodeveloper
Copy link

unicodeveloper commented Jun 17, 2018

Hey @lorensr IMO, if you are using withAuth once, why make it at all? I'd rather not allow it to exist.

One authentication service, like Auth.js with all the helpers needed can do the magic. Check this Auth Service file.

And then comfortably bring in the withUser HOC!

@lorensr
Copy link
Member Author

lorensr commented Jun 17, 2018 via email

@unicodeveloper
Copy link

No, I don't. It's just a matter of trade-offs.

Do you want to prevent prop drilling because of how stressful and confusing it can be in the long run? If yes, then just use the withUser HOC anywhere you need it.

@lorensr
Copy link
Member Author

lorensr commented Aug 13, 2018

Okay, here's the plan! I'm liking it a lot. Lmk if you have any further thoughts @unicodeveloper @jeresig

  • direct user query
  • login/logout: globals
  • loginInProcess status: apollo-link-state
  • withUser: cache-only HOC
// withUser.js
export const USER_QUERY = gql`
  query UserQuery {
    currentUser {
      firstName
      name
      username
      email
      photo
      hasPurchased
    }
    loginInProcess @client
  }
`

export const withUser = graphql(USER_QUERY, {
  options: { fetchPolicy: 'cache-only' },
  props: ({ data: { currentUser, loginInProcess, loading } }) => ({
    user: currentUser,
    loggingIn: loginInProcess || loading
  })
})
// auth.js
const fetchUserData = () =>
  apollo.query({
    query: USER_QUERY,
    fetchPolicy: 'network-only'
  })

// fetch at pageload in case client is already logged in
fetchUserData()

export const login = () => {
  apollo.writeData({ data: { loginInProcess: true } })
  auth0Login({
    onCompleted: e => {
      apollo.writeData({ data: { loginInProcess: false } })
      if (e) {
        console.error(e)
        return
      }

      apollo.reFetchObservableQueries()
      fetchUserData()
    }
  })
}

export const logout = () => {
  auth0Logout()
  apollo.resetStore()
}

And then in components:

import { withUser } from '../lib/withUser'
import { login, logout } from '../lib/auth'

class Profile extends Component


export default withUser(Profile)

This simplifies App.js/routing a lot and completely eliminates passing around login/logout/user/loggingIn everywhere.

@lorensr
Copy link
Member Author

lorensr commented Aug 28, 2018

Turned out to be simpler—given deduplication and default cache-first fetch policy, there's no point to the fetchUserData and fetchPolicy: 'cache-only'

lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
lorensr added a commit that referenced this issue Mar 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants