zoukankan      html  css  js  c++  java
  • 前端(十一):props、state及redux关系梳理

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

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

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

      两者的结果完全一致。

  • 相关阅读:
    排序算法——选择排序
    poj1906
    poj1496
    poj1244
    poj1183
    poj1806
    !!!舒尔茨自律神经训练法
    Google Analytics的能与不能
    乔布斯最伟大的贡献是什么
    冥想呼吸
  • 原文地址:https://www.cnblogs.com/kuaizifeng/p/9417127.html
Copyright © 2011-2022 走看看