React項目實踐(二)一個登陸頁面的狀態遷移

上一篇React項目實踐(一)從框架配置講起javascript

此次談談一個登陸頁面的設計。在以前寫過的項目中,我都是把表單放在一個頁面也沒有考慮到用戶等待過程當中的loading提示。此次從新規劃一下,將展現組件與控制拆分,同時用狀態機實現狀態管理前端

狀態分析

前幾天恰好看到一篇文章前端狀態管理請三思,以爲挺有意思的,原文做者利用狀態機的思想,預先設想好全部狀態和狀態的遷移,優雅的管理頁面登陸狀態避免過多變量的使用。本文參考做者的思想和代碼,實現一個簡單的登陸頁面。狀態分析以下:java

  • 初始登陸頁面是展現登陸的表單(login form)
  • 當提交(submit)數據過程後,頁面變爲等待數據響應狀態(loading)
    • 數據響應有兩種狀態,成功(success)頁面跳轉到首頁;失敗(failure)頁面提示錯誤
    • 當登陸成功,只有先退出登陸(logout)以後才能從新登陸
    • 當登陸失敗,從新提交(submit)回到加載狀態(loading)
  • logout以後回到login form狀態

依舊是模仿掘金app登陸頁面的一個實現效果:
react

登陸成功效果

定義狀態機

const machine = {
  states: {
    'login form': {
      submit: 'loading'
    },
    loading: {
      success: 'profile',
      failure: 'error'
    },
    profile: {
      viewProfile: 'profile',
      logout: 'login form'
    },
    error: {
      submit: 'loading'
    }
  }
}
複製代碼

實現一個狀態控制函數,返回下一個狀態git

const stateTransformer = function(currentState, stepUp) {
  let nextState
  if (machine.states[currentState][stepUp]) {
    nextState = machine.states[currentState][stepUp]
  }
  console.log(`${currentState} + ${stepUp} --> ${nextState}`)
  return nextState || currentState
}
複製代碼

咱們把狀態控制的變量存儲在redux中,定義一個簡單的auth模塊以下,stateChanger純函數用於控制currentState的狀態遷移,每次操做結果返回進行狀態變換github

export default {
  namespace: 'auth',
  state: {
    currentState: 'login form'
  },
  reducers: {
    stateChanger(state, {stepUp}) {
      return {
        ...state,
        currentState: stateTransformer(state.currentState, stepUp)
      }
    }
  },
  effects: dispatch => ({
  async loginByPhoneNumber(playload, state) {
    dispatch.auth.stateChanger({stepUp: 'submit'})
    let {data} = await api.auth.loginByPhoneNumber(playload)
    if (data.s === 0) {
      dispatch.auth.stateChanger({stepUp: 'success'})
      saveData('juejin_token', data.token)
    } else {
      dispatch.auth.stateChanger({stepUp: 'failure'})
      Toast.info('用戶名或密碼錯誤', 2)
    }
  }
})
}
複製代碼

那麼在組件中,咱們很容易寫一個控制狀態變化的組件json

render() {
    let {currentState} = this.props
    return (
      <>
        {(() => {
          switch (currentState) {
            case 'loading':
              return (
                //加載中展現組件
              )
            case 'profile':
              return <Redirect to={'/'} />//返回首頁
            default:
              return (
                //登陸表單
              )
          }
        })()}
      </>
    )
  }
複製代碼

具體配置補充

爲了配合項目的用戶登陸驗證,咱們從新搭建一個本地服務,在react配置路由的代理轉發,具體地,在根目錄下新建文件src/setupProxy.js,將/api開頭請求轉發到服務器redux

const proxy = require('http-proxy-middleware')
module.exports = function(app) {
  app.use(proxy('/api', {target: 'http://localhost:8989/', changeOrigin: true}))
}
複製代碼

services/api定義數據接口後端

export async function loginByPhoneNumber({phoneNumber, password}) {
  return post('/api/auth/type/phoneNumber', {
    body: {
      phoneNumber,
      password
    }
  })
}
複製代碼

後端實現一個簡單的中間件路由api

const Koa = require('koa')
const router = require('./router')
router.post('/auth/type/phoneNumber', async (ctx, next) => {
  var {phoneNumber, password} = await parse.json(ctx.req)
  if (phoneNumber === '15111111111' && password === '123456') {
    let token = generateToken({uid: phoneNumber, password})
    ctx.response.body = JSON.stringify({
      s: 0,
      m: `帳號登陸成功錯誤`,
      d: '',
      token
    })
  } else {
    ctx.response.body = JSON.stringify({s: 1, m: '帳號信息錯誤', d: ''})
  }
})
複製代碼

項目地址 總之是我的的一個小實踐,你們在登陸頁面的管理中有什麼更好的作法嗎?還有什麼複雜的狀況須要考慮

相關文章
相關標籤/搜索