前端(九):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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...