React: Simple Auth Flow

koralarts

Karl Castillo

Posted on March 23, 2020

React: Simple Auth Flow

Now that we know how to use useState, useReducer and Context, how can we put these concepts into our projects? An easy example is to create a simple authentication flow.

We'll first setup the UserContext using React Context.

import { createContext } from 'react'

const UserContext = createContext({
  user: null,
  hasLoginError: false,
  login: () => null,
  logout: () => null
})

export default UserContext
Enter fullscreen mode Exit fullscreen mode

Now that we've created a context, we can start using it in our wrapping component. We'll also use useReducer to keep the state of our context.

import UserContext from './UserContext'

const INITIAL_STATE = {
  user: null,
  hasLoginError: false
}

const reducer = (state, action) => { ... }

const App = () => {
  const [state, dispatch] = useReducer(reducer, INITIAL_STATE)

  return (
    <UserContext.Provider>
      ...
    </UserContext.Provider>
  )
}
Enter fullscreen mode Exit fullscreen mode

Our reducer will handle 2 action types -- login and logout.

const reducer = (state, action) => {
  switch(action.type) {
    case 'login': {
      const { username, password } = action.payload
      if (validateCredentials(username, password)) {
        return {
          ...state,
          hasLoginError: false,
          user: {} // assign user here
        }
      }

      return {
        ...state,
        hasLoginError: true,
        user: null
      }
    }
    case 'logout':
      return {
        ...state,
        user: null
      }
    default:
      throw new Error(`Invalid action type: ${action.type}`)
  }
}
Enter fullscreen mode Exit fullscreen mode

After implementing the reducer, we can use dispatch to call these actions. We'll create functions that we'll pass to our provider's value.

...
const login = (username, password) => {
  dispatch({ type: 'login', payload: { username, password } })
}
const logout = () => {
  dispatch({ type: 'logout' })
}

const value = {
  user: state.user,
  hasLoginError: state.hasLoginError,
  login,
  logout
}

return (
  <UserContext.Provider value={value}>
    ...
  </UserContext.Provider>
)
Enter fullscreen mode Exit fullscreen mode

Now that our value gets updated when our state updates, and we passed the login and logout function; we'll have access to those values in our subsequent child components.

We'll make two components -- LoginForm and UserProfile. We'll render the form when there's no user and the profile when a user is logged in.

...
<UserContext.Provider value={value}>
  {user && <UserProfile />}
  {!user && <LoginForm />}
</UserContext.Provider>
...
Enter fullscreen mode Exit fullscreen mode

Let's start with the login form, we'll use useState to manage our form's state. We'll also grab the context so we have access to login and hasLoginError.

const { login, hasLoginError } = useContext(UserContext)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')

const onUsernameChange = evt => setUsername(evt.target.value)
const onPasswordChange = evt => setPassword(evt.target.value)
const onSubmit = (evt) => {
  evt.preventDefault()
  login(username, password)
}

return (
  <form onSubmit={onSubmit}>
    ...
    {hasLoginError && <p>Error Logging In</p>}
    <input type='text' onChange={onUsernameChange} />
    <input type='password' onChange={onPasswordChange} />
    ...
  </form>
)
Enter fullscreen mode Exit fullscreen mode

If we're logged in we need access to the user object and the logout function.

const { logout, user } = useContext(UserContext)

return (
  <>
    <h1>Welcome {user.username}</h1>
    <button onClick={logout}>Logout</button>
  </>
)
Enter fullscreen mode Exit fullscreen mode

Now, you have a simple authentication flow in React using different ways we can manage our state!

Code Sandbox

💖 💪 🙅 🚩
koralarts
Karl Castillo

Posted on March 23, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related