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

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

  在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. centos7修改静态ip地址

    今天逛园的时候突然发现这篇有关网络参数修改的文章写的很好,简单又使用,格式也很好的,所以就引用过来了. http://www.cnblogs.com/hongdada/p/6666932.html

  2. 2018 ACM-ICPC 亚洲区域赛北京现场赛 I题 Palindromes

    做法:打表找规律 大数是过不了这个题的(但可以用来打表) 先找k的前缀,前缀对应边缘数字是哪个 如果第0位是2-9 对应奇数长度的1-8 第0位为1时,第1位为0时对应奇数长度的9,为1-9时对应偶数 ...

  3. [JS] 理解jquery的$.extend()、$.fn和$.fn.extend()

    jQuery为开发插件提拱了两个方法,分别是: jQuery.fn.extend(); jQuery.extend(); jQuery.fn jQuery.fn = jQuery.prototype ...

  4. Java 文件本地上传、下载和预览的实现

    以下方法为通用版本 实测图片和pdf 都没有问题 上传方法需要前端配合post请求 ,下载前端用a标签就可以,预览 前端使用ifrme标签   ,就可以实现基本功能... 1.文件本地上传 publi ...

  5. Welcome! This is the documentation for Python 3.6.8

    The Zen of Python, by Tim Peters Beautiful is better than ugly.Explicit is better than implicit.Simp ...

  6. github常用项目汇总

    1.smartTable(智能表格) android自动生成表格框架 使用方法:在github中搜索smartTable 进入项目后,查看开源项目的介绍和使用方法即可.

  7. axiso 生产环境跨域配置(可用)

    1.npm install axios 后 在main.js中import import Axios from 'axios'Vue.prototype.$http = Axios 2.请求配置 th ...

  8. 关于开发环境无法运行applet

    测试一下IE java vm 如果没有正确显示出来,说明java vm插件没有装好: 我用Uninstalle 来清理注册表:重装jdk 1.6_45

  9. 剑指offer——面试题27:二叉树的镜像

    函数递归 void MirrorIteratively(BinaryTreeNode* pRoot) { if(pRoot == nullptr) return; std::stack<Bina ...

  10. 何为Web App,何为Hybird App

    这些概念听起来很火,当下也很流行,真正理解起来却并非易事.如果让我来全面的解释Web App和Hybird App,我觉得还有些困难. 这篇文章只是我深入了解移动领域开发过程中的不断整理和总结,其中涉 ...