所谓状态机,是一种抽象的数据模型,是“事物发展的趋势”,其原理是事件驱动。广泛地讲,世界万物都是状态机。

一、状态机是一种抽象的数据模型

  在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')
);

  两者的结果完全一致。

前端(十一):props、state及redux关系梳理的更多相关文章

  1. 8. react 基础 - props 默认值和类型限制 与 Props , State , render 函数 关系

    一. PropTypes 与 DefaultProps 官方文档 1. PropTypes 属性校验 引入 PropTypes import PropTypes from 'prop-types'; ...

  2. nginx、fastCGI、php-fpm关系梳理(转载参考)

    nginx.fastCGI.php-fpm关系梳理 还可以参考:http://www.cnblogs.com/skynet/p/4173450.html   前言: Linux下搭建nginx+php ...

  3. MySQL、sqlalchemy、pymysql、mysqldb、DBAPI之间关系梳理(终于明白了)

    MySQL.sqlalchemy.pymysql.mysqldb.DBAPI之间关系梳理(终于明白了) python3不再支持mysqldb 请用pymysql和mysql.connector 问题背 ...

  4. 关于props和state以及redux中的state

    React的数据模型分为共有数据和私有数据,共有数据可以在组件间进行传递,私有数据为当前组件私有.共有数据在React中使用props对象来调用,它包含标签所有的属性名称和属性值,props对象有三个 ...

  5. react中 props,state与render函数的关系

    我们很明显的能够感受到,react是一门数据驱动的框架,当数据发生变化,页面就会自动发生变化,他背后的原理是怎么样子的呢 比如todolist例子里面,inputValue变了,框里面的内容就会自动变 ...

  6. Redux 关系图解

    Redux是一款状态管理库,并且提供了react-redux库来与React亲密配合, 但是总是傻傻分不清楚这2者提供的API和相应的关系.这篇文章就来理一理. Redux Redux 三大核心 Re ...

  7. [Redux] Accessing Dispatch and State with Redux -- connect

    If you have props and actions, you want one component to access those props and actions, one solutio ...

  8. Redux的梳理

    学习Redux之前,我了解了它需要去解决什么问题: 用户使用方式复杂 不同身份不同使用方式 多个用户可以协作 与服务器大量交互,或者使用websocket 视图数据从多个来源获取 共享组件状态 组件之 ...

  9. React Native props & state

    今天又敲了一丁点代码,看了一下props和state的用法 原本以为state只是一个状态,但是又阅读了一下原文,才知道state是一组状态,这些状态是开发者自己定义的,都统一在state这个大类底下 ...

随机推荐

  1. 洛谷P1742 最小圆覆盖(计算几何)

    题面 传送门 题解 之前只是在抄题解--这篇才算是真正自己想的吧-- 首先我们把输入序列给\(random\)一下防止出题人好心送你一个毒瘤序列 我们设\(r\)为当前最大半径,\(o\)为此时对应圆 ...

  2. rpm 卸载

    卸载:python-urlgrabber-3.9.1-9.el6.noarch rpm -e python-urlgrabber-3.9.1-9.el6.noarch

  3. C语言 从头学起了

    1,我用的是VC6.0++编译器 具体安装使用教程 去网上找 2,刚写hello world时候 就遇到一个坑. #网上 hello world 代码 #include<stdio.h> ...

  4. 图解JVM内存分配和回收

    一.简介 JVM采用分代垃圾回收.在JVM的内存空间中把堆空间分为年老代和年轻代.将大量(据说是90%以上)创建了没多久就会消亡的对象存储在年轻代,而年老代中存放生命周期长久的实例对象.年轻代中又被分 ...

  5. jquery源码解析:attr,prop,attrHooks,propHooks详解

    我们先来看一下jQuery中有多少个方法是用来操作元素属性的. 首先,看一下实例方法: 然后,看下静态方法(工具方法): 静态方法是内部使用的,我们外面使用的很少,实例方法才是对外的. 接下来,我们来 ...

  6. AssertJ断言系列-----------<数据库断言三>

    其实,是有很多种数据断言的使用.那么,我们在接口的测试中,到底应不应该加上数据库断言呢?我的观点是,视情况而定:某一些特殊的场景或者特殊的业务,那么我们就一定要加上数据库断言.不是我们测试人员,不相信 ...

  7. 【Quartz】基本原理

    1 核心概念 1.1    核心元素 (1)Scheduler 任务调度器,是Quartz框架的核心,负责管理其他组件. (2)Trigger 触发器,用于定义任务调度的时间规则,有SimpleTri ...

  8. 2016级算法第六次上机-G.ModricWang likes geometry

    1116 ModricWang likes geometry 思路 难题,非常考察几何知识,放在这里作为计算几何场次的最难的题. 原题地址 原版题解 代码

  9. 编程开发之--java多线程学习总结(5)

    4.对继承自Runnable的线程进行锁机制的使用 package com.lfy.ThreadsSynchronize; import java.util.concurrent.locks.Lock ...

  10. L08-Linux解决Device eth0 does not seem to be present,delaying initialization问题

    问题前提: 在VirtualBox中克隆Linux服务器,如下,由Centos6.5_Base克隆得到node01服务器,采用的是完全克隆的方式,克隆时重新初始化MAC地址. 原服务器Centos6. ...