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

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

  在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. oracle根据四位年周取当周周一的日期函数

    create or replace function FUNC_GET_DATE_BY_WEEK( theYearWeek IN VARCHAR2)return date is normalDate ...

  2. zTree第一章,纯静态

    zTree v3.5 Demo 演示 http://www.treejs.cn/v3/demo.php#_101 ------------------------------------------- ...

  3. vue + ElementUI 关闭对话框清空验证,清除form表单

    前面跟大家提到过 elementUI验证的问题,那么今天就来看看 点击对话框和关闭按钮 怎么清空验证,清空form表单,避免二次点击还会有 验证错误的提示 1.首先在你的对话框 取消按钮 加一个cli ...

  4. Linux下配置环境变量的几个方法实例

    场景:一般来说,配置交叉编译工具链的时候需要指定编译工具的路径,此时就需要设置环境变量.例如我的mips-linux-gcc编译器在“/opt/au1200_rm/build_tools/bin”目录 ...

  5. Asp.net的生命周期之页生命周期

    参考:http://msdn.microsoft.com/zh-cn/library/ms178472%28v=vs.100%29.aspx http://msdn.microsoft.com/zh- ...

  6. PHP中filesystem的使用

    PHP中filesystem的使用 最近在用腾讯云COS上传对象的时候,涉及到文件流的使用 参考文档:https://cloud.tencent.com/document/product/436/12 ...

  7. 06-图2 Saving James Bond - Easy Version (25 分)

    This time let us consider the situation in the movie "Live and Let Die" in which James Bon ...

  8. Codeforces Round #555 (Div. 3) A B C1(很水的题目)

    A. Reachable Numbers 题意:设f(x)为 x+1 这个数去掉后缀0的数,现在给出n,问经过无数次这种变换后,最多能得到多少个不同的数. 代码 #include<cstdio& ...

  9. prim /kruskal 最小生成树

    #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #inc ...

  10. Docker 入门相关

    什么是Docker和容器 可能是把Docker的概念讲的最清楚的一篇文章 为什么要用Docker 相关网址 Window平台Docker下载 一些基本知识 Volume docker volume l ...