前端(九):redux使用實例

来源:https://www.cnblogs.com/kuaizifeng/archive/2018/08/04/9417127.html
-Advertisement-
Play Games

開頭先寫一句理論:所謂狀態機,是一種抽象的數據模型,是“事物發展的趨勢”,其原理是事件驅動。廣泛地講,世界萬物都是狀態機。 一、狀態機是一種抽象的數據模型 在react中,props和state都可以用來傳遞數據。這裡作一下區分。 1.props props用於組件間的數據傳遞。其本身只是一個屬性, ...


  所謂狀態機,是一種抽象的數據模型,是“事物發展的趨勢”,其原理是事件驅動。廣泛地講,世界萬物都是狀態機。

一、狀態機是一種抽象的數據模型

  在react中,props和state都可以用來傳遞數據。這裡作一下區分。

  1.props

  props用於組件間的數據傳遞。其本身只是一個屬性,不是一個狀態機。

  從子組件的角度看,子組件無法擅自修改父組件通過屬性傳遞的數據,因此具有單向數據流的特點。

  2.state

  state用於設置組件本身的狀態。state用於用戶數據交互、事件監聽。

  setState會調用render()方法以重新渲染。因此,setState不能寫到render()裡面。

  當state數據發生改變時,該組件和state數據作用域內的子組件都會一層一層地重新渲染。

  3.state與props

  props傳遞state中的數據時,如果數據發生改變,子組件會被重新渲染。

  子組件可以通過調用父組件的方法來修改父組件傳遞過來的數據。

import React from 'react'
import ReacDOM from 'react-dom'
import { Button } from 'antd-mobile'

class SubCom extends React.Component{
constructor(props){
super(props);
this.state={ name: "Jan" }
}
render(){
// 將子組件數據傳遞給父組件
console.log("我被重新渲染了");
return <Button type='primary' onClick={()=>this.props.handleChange("name", this.state.name)}>點我</Button>
}
}
// 通過函數改變state狀態修改父組件傳遞的值
class App extends React.Component{
constructor(props){
super(props);
this.state = {
string: "我是一個button"
};
this.handleChange = this.handleChange.bind(this)
}
handleChange(key, val){
this.setState({
string: "我是一個藍色的button",
key: val
})
};
render(){
console.log(this.state);
return <SubCom handleChange={ this.handleChange }/>
}
}
ReacDOM.render(
<App />,
document.getElementById("root")
);

  4、state的局限性

  state作為單個組件的狀態機,關註的只是單個組件內部。如果一個子組件需要修改一個父組件的state,那麼父組件就需要將handleChange一級一級地傳遞給這個組件,並且要保證整個過程不會被其它狀態或屬性干擾。並且當父組件的state發生改變時,其到這個自組件的所有中間組件都要重新渲染,這顯然不符合我們的需要。

  因此,在複雜的數據交互中,state就顯得力不從心。這時,一種更為抽象的數據模型應運而生,那就是redux。

  5、redux插件

  redux、redux-thunk、react-redux一起解決了上述問題。

  redux-thunk、react-redux主要工作是建立非同步狀態機,並能夠只重新渲染state狀態涉及的子組件,而其它無關中間組件則不會重新渲染。

  redux代表著更為抽象的數據模型,它的主要內容有兩個:一是打破組件內部this.state的孤立性,使得各層級的組件能夠共用一個state;二是解耦,將一些公用的狀態抽離成一個狀態樹,專門處理特定的數據。

  6、redux狀態機與props、state的關係

  redux狀態機是抽離的公用的state。

  和組件內的state一樣,需要用props來傳遞,這種傳遞只有一層:整個app的最外層provider,以及被connect裝飾的子組件。

  可以從子組件的props中獲取redux狀態機中的state數據。

二、使用實例

  一個用戶註冊、登錄和修改個人信息的狀態機。

// src/reducer.js
import axios from 'axios';
import {getRedirectPath} from '../utils/userRedirect'

const ERROR_MSG = 'ERROR_MSG';
const LOAD_COOKIE = "LOAD_COOKIE";
const AUTH_SUCCESS = "AUTH_SUCCESS";
const CLEAR_COOKIE = "CLEAR_COOKIE";

// 獲取用戶登錄信息
const initState = {
msg: '',
user:'',
type:'',
redirectTo:''
};

export function user(state=initState, action) {
switch (action.type){
case ERROR_MSG:
return {...state, isAuth: false, msg: action.msg};
case LOAD_COOKIE:
return {...state, ...action.payload};
case AUTH_SUCCESS:
return {...state, ...action.payload, redirectTo: getRedirectPath(action.payload)}; // getRedirectPath是根據返回data中的用戶類型返回相應url的處理函數
case CLEAR_COOKIE:
return {...initState, redirectTo:'/login'}; // 將登錄信息清空,回到初始狀態,並重定向到login
default:
return state;
}
}

// 假如註冊、登錄和更新數據的action函數以及返回的狀態都一樣,可以把它們合併到一起。
function authSuccess(data){
return {type: AUTH_SUCCESS, payload:data}
}
function errMsg(msg) {
return {type: ERROR_MSG, msg}
}

// 註冊時獲取用戶信息
export function register({user, pwd, repeatPwd, type}) {
if(!user || !pwd || !type){
return errMsg("用戶名和密碼不能為空")
}
if(pwd !== repeatPwd){
return errMsg('密碼和確認密碼不一致')
}
return dispatch=>{
axios.post('/user/register', {user, pwd, type}).then(res=>{
if(res.status===200 && res.data.code===0){
dispatch(authSuccess(res.data.data))
}else {
dispatch(errMsg(res.data.msg))
}
})
}
}
// 登錄時獲取用戶信息
export function login({user, pwd}) {
if(!user || !pwd){
return errMsg("用戶名密碼必須輸入")
}
return dispatch=>{
axios.post('/user/login', {user, pwd}).then(res=>{
if(res.status===200 && res.data.code===0){
// console.log(res.data.data);
dispatch(authSuccess(res.data.data)) // loginSuccess改成authSuccess
}else {
dispatch(errMsg(res.data.msg))
}
})
}
}
// 更新數據
export function update(data) {
return dispatch=>{
axios.post('user/update', data).then(res=>{
if(res.status===200 && res.data.code===0){
dispatch(authSuccess(res.data.data))
}else {
dispatch(errMsg(res.data.msg))
}
})
}
}

// 讀取cookie
export function loadCookie(data) {
return {type: LOAD_COOKIE, payload: data}
}
// 清除cookie
export function clearCookie() {
return { type: CLEAR_COOKIE }
}

  狀態機的使用例子。這裡沒有server端。

import React from 'react'
import ReacDOM from 'react-dom'
import {createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import { Provider, connect } from 'react-redux'

import {List, InputItem, WingBlank, WhiteSpace, Button, NavBar } from 'antd-mobile'
import { login } from "./reducer";

const store = createStore(user, compose(
applyMiddleware(thunk),
window.devToolsExtension?window.devToolsExtension():f=>f));

@connect(state=>state, { login })
class App extends React.Component{
constructor(props){
super(props);
this.state={
user: '',
pwd: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleLogin = this.handleLogin.bind(this);
}
handleChange(key, val){
this.setState({[key]: val})
}
handleLogin(){
this.props.login(this.state)
}
render(){
return (
<div>
<WingBlank>
<NavBar mode="dark">登錄頁面</NavBar>
<List>
<WhiteSpace />
<InputItem onChange={v=>this.handleChange('user', v)}>用戶</InputItem>
<WhiteSpace />
<InputItem onChange={v=>this.handleChange('pwd', v)} type='passwd'>密碼</InputItem> <WhiteSpace />
<Button type='primary' onClick={this.handleLogin}>登錄</Button>
</List>
</WingBlank>
</div>
)
}

}

ReacDOM.render(
<Provider store={ store }>
<App />
</Provider>,
document.getElementById("root")
);

 三、redux簡單實現

  1、redux簡單用例


import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'

function reducer(state=0, action) {
// action都是事件類型
switch(action.type){
case 'addBBQ':
return state + 1;
case 'reduceBBQ':
return state - 1;
default: return 10
}
}

// 1.新建atore
const store = createStore(reducer);
// 2.派發事件 傳遞action
store.dispatch({type: "addBBQ"});
store.dispatch({type: "reduceBBQ"});
// 3.訂閱消息
function listener() {
const current = store.getState(); // 獲取狀態
console.log(`現在的BBQ數量是${current}`);
}
// 4.監聽變更
store.subscribe(listener);
// 5.監聽派發事件
store.dispatch({type: "reduceBBQ"});
store.dispatch({type: "reduceBBQ"});
store.dispatch({type: "reduceBBQ"});

ReactDOM.render(
<p> demo </p>,
document.getElementById('root')
);

  2、redux簡單實現

import React from 'react'
import ReactDOM from 'react-dom'

function reducer(state=0, action) {
switch(action.type){
case 'addBBQ':
return state + 1;
case 'reduceBBQ':
return state - 1;
default: return 10
}
}
// 寫一個簡單的redux
function createStore(reducer) {
let currentState = {};
let currentListeners = [];
function getState() {
return currentState;
}
function subscribe(listener) {
currentListeners.push(listener);
}
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach(v=>v())
}
dispatch({type: '@#$%^&*('}); // 執行一遍獲取預設state
return { getState, subscribe, dispatch }
}

// 1.新建atore
const store = createStore(reducer);
// 2.派發事件 傳遞action
store.dispatch({type: "addBBQ"});
store.dispatch({type: "reduceBBQ"});
// 3.訂閱消息
function listener() {
const current = store.getState(); // 獲取狀態
console.log(`現在的BBQ數量是${current}`);
}
// 4.監聽變更
store.subscribe(listener);

// 5.監聽派發事件
store.dispatch({type: "reduceBBQ"});
store.dispatch({type: "reduceBBQ"});
store.dispatch({type: "reduceBBQ"});

ReactDOM.render(
<p> demo </p>,
document.getElementById('root')
);

  兩者的結果完全一致。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、概述 提高網路性能優化,很重要的一點就是降低延遲和提升響應速度。 通常我們在瀏覽器中發起請求的時候header部分往往是這樣的 keep-alive 就是瀏覽器和服務端之間保持長連接,這個連接是可以復用的。在HTTP1.1中是預設開啟的。 連接的復用為什麼會提高性能呢? 通常我們在發起http請 ...
  • 創建Key SHA1查看器下載地址 高德Jar包和so文件下載地址 許可權和服務 實例 ...
  • 一、問題 ①java代碼沒有顏色區分,統一黑色 ②代碼不會聯想提示,原來打前幾個字母便會聯想到後面的內容 二、解決 打開File,將Power save Mode的勾勾去掉 ...
  • 人臉裁剪類 ...
  • WIFI管理類 連接指定WIFI實例 回調介面 彈框驗證 ...
  • 在之前的章節,我講解瞭如何為Android或者iOS應用程式開發準備環境以及Layout佈局的一些基本概念。在本章中,我將開始講解和介紹Xamarin中頁面(Page)相關的介紹。 ...
  • UICollectionView 與 UITableView的異同 相同點: 不同點: ① 動過代理和數據源方法來實現UI和數據填充的; ② 對Cell的重利用,實現了迴圈利用優化; 不同點: ① UITableView是系統自定義的豎直佈局,只能豎直滾動,UICollectionView可以自由選 ...
  • GsonFormat插件用於在androidStudio 根據json自動生成class的欄位和方法,極大提高了開發效率 一、安裝GsonFormat插件 二、重啟Android Studio,新建一個java類 ,然後在文件內 按快捷鍵alt+Insert 彈出菜單選擇GsonFormat ,複製 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...