前端(九):redux使用实例
开头先写一句理论:所谓状态机,是一种抽象的数据模型,是“事物发展的趋势”,其原理是事件驱动。广泛地讲,世界万物都是状态机。
一、状态机是一种抽象的数据模型
在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.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 }
}
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")
);
上一篇: Python--初识函数
下一篇: Python中的迭代器