开头先写一句理论:所谓状态机,是一种抽象的数据模型,是“事物发展的趋势”,其原理是事件驱动。广泛地讲,世界万物都是状态机。
一、状态机是一种抽象的数据模型
在react中,props和state都可以用来传递数据。这里作一下区分。
1.props
props用于组件间的数据传递。其本身只是一个属性,不是一个状态机。
从子组件的角度看,子组件无法擅自修改父组件通过属性传递的数据,因此具有单向数据流的特点。
2.state
state用于设置组件本身的状态。state用于用户数据交互、事件监听。
当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.jsimport 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)) } }) }} // 读取cookieexport function loadCookie(data) { return {type: LOAD_COOKIE, payload: data}}// 清除cookieexport function clearCookie() { return { type: CLEAR_COOKIE }}
code===0){ // console.log(res.data.data); dispatch(authSuccess(res.data.data)) // 将loginSuccess改成authSuccess }else { dispatch(errMsg(res.data.msg)) } }) }}// 读取cookieexport function loadCookie(data) { return {type: LOAD_COOKIE, payload: data}}// 更新数据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)) } }) }}// 清除cookieexport 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"));
原文地址:https://www.cnblogs.com/kuaizifeng/p/9417127.html