React学习总结

作者: 读书的鱼 | 来源:发表于2019-02-28 04:36 被阅读334次

    第1章 课程导学

    学习流程
    相关的知识点

    学习前提
    要有一些js、es6、webpack、npm等基础知识

    第2章 React初探

    2-1 React简介

    React
    FaceBook 推出
    2013年开源
    函数式编程
    使用人数最多的前端框架之一
    健全的文档与完善的社区

    2-2 React开发环境准备

    1)、确定自己电脑已经安装了node环境
    根据自己电脑来安装属于自己电脑版本的node,推荐使用稳定版
    node官网下载地址
    2)、安装react脚手架create-react-app

    npx create-react-app my-app
    cd my-app
    npm start
    
    2-3 工程目录文件简介

    因为通过脚手架下载的程序,有很多文件是需要我们剔除出去的。
    例如没用的样式文件、测试文件、pwa相关的一些文件等等

    • 项目结构
    • ├── node_modules # 第三方的依赖库文件
    • ├── public
    • │ ├── favicon.ico # Favicon
    • │ ├── index.html # 首页
    • ├── src
    • │ ├── app.js # 业务代码文件
    • │ ├── index.js # 应用入口
    • ├── README.md
    • └── .gitignore #git忽略提交的文件配置
    • └── package.json #启动、编译命令设置;依赖包管理配置信息
    • └── yarn.lock #yarn 依赖包管理配置信息
    2-4 React中的组件

    下面就是定义了一个App的组件

    //App.js
    import React, {Component} from 'react'
    
    class App extends Component {
        render() {
          //JSX 语法
          return (
              <div>hello world!</div>
          )
        }
    }
    export default App
    

    App组件引用和调用

    //index.js
    import React from 'react'
    import ReactDOM from 'react-dom'
    
    import App from 'App.js'
    
    ReactDOM.render(<App />,document.getElementById('root'))
    

    JSX语法在使用的时候,组件的定义和使用的时候要大写
    JSX语法中,我们要求一个组件render函数返回的内容,外层必须要有一个大的元素包裹(div或者Fragment)
    Fragment渲染到页面的时候是不占位的
    eg:

    //index.js
    import App from 'App.js'  //大写
    
    ReactDOM.render(<App />,document.getElementById('root'))
    

    第3章 React基础精讲

    3-1 使用React编写TodoList功能
    //TodoItem.js
    import React, {Component} from 'react';
    import PropTypes from 'prop-types'; //组件类型进行校验
    
    class TodoItem extends Component {
    
        constructor(props) {
            super(props);
            this.handleDelete = this.handleDelete.bind(this)
        }
    
        render() {
            const {content, test} = this.props;
    
            return (
                <div onClick={this.handleDelete}>{test}-{content}</div>
            )
        }
    
        handleDelete() {
            const {index, deleteItem} = this.props;
            deleteItem(index)
        }
    }
    
    TodoItem.propTypes = {
        test: PropTypes.string.isRequired, //父组件没有给子组件传递test 但是又是必填项,所以可以通过defaultProps给一个默认值
        content: PropTypes.string,
        deleteItem: PropTypes.func,
        index: PropTypes.number
    };
    
    TodoItem.defaultProps = {
        test: 'hello'
    };
    export default TodoItem
    
    
    //TodoList.js
    import React, {Component, Fragment} from 'react';
    
    import TodoItem from './TodoItem';
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
            this.state = {
                inputValue: '',
                list: []
            };
            this.handleInputChange = this.handleInputChange.bind(this);
            this.handleAdd = this.handleAdd.bind(this);
            this.handleDelete = this.handleDelete.bind(this);
        }
    
        render() {
            return (
                <Fragment>
                    <div>
                        <label htmlFor="inputArea">输入内容:</label>
                        <input
                            id="inputArea"
                            type="text"
                            value={this.state.inputValue}
                            onChange={this.handleInputChange}
                        />
                        <button onClick={this.handleAdd}>提交</button>
                    </div>
                    <ul>
                        {this.getTodoItem()}
                    </ul>
                </Fragment>
            )
        }
    
        getTodoItem() {
            return this.state.list.map((item, index) => {
                return (
                    <TodoItem
                        content={item}
                        key={index}
                        index={index}
                        deleteItem={this.handleDelete}
                    />
                )
            })
        }
    
        handleInputChange = (e) => {
            const value = e.target.value;
            this.setState(() => {
                return {
                    inputValue: value
                }
            })
        };
    
        handleAdd = () => {
            this.setState((prevState) => {
                return {
                    list: [...prevState.list, prevState.inputValue],
                    inputValue: ''
                }
            });
        };
    
        handleDelete = (index) => {
            this.setState((prevState) => {
                const list = [...prevState.list];
                list.splice(index, 1);
                return {list}
            });
        }
    }
    
    export default TodoList;
    
    
    3-2 JSX语法细节补充

    1.注释:必须要用花括号括起来
    单行注释

    {
    //这里是单行注释
    }
    

    多行注释

    {/*这是多行注释*/}
    

    2.样式名称引入:将class改为className

    因为class这个和定义组件“类”有冲突

    <input className="inputClass" />
    

    3.表单label for属性的引用,要将for改为htmlFor
    防止for和js中循环for引起冲突

    <label htmlFor="inputArea">用户名</label>
    <input id="inputArea" />
    

    4.将input标签内容直接进行转义呈现在页面 dangerouslySetInnerHTML

     <li
         key={index}
         onClick={this.handleDeleteItem.bind(this, index)}
         dangerouslySetInnerHTML={{__html: item}}
    >
    </li>
    
    3-3 组件拆分与组件之间的传值

    1.组件拆分
    当一个页面很大的时候,那么我们就会对这个页面进行组件的拆分,拆分的结构就像一棵树一样


    组件之间的结构

    2.组件之间的传值
    父组件向子组件传值是通过“子组件的属性”
    子组件接受父组件传过来的值:this.props.属性名称
    子组件向父组件传值是通过 子组件调用父组件的方法,从来改变父组件的数据,但是要对这个方法调用的时候,父组件要对这个方法进行this的绑定

    3-4 TodoList 代码优化
    //解构优化
    this.props.content ,this.props.index
    改为
    const {content,index} = this.props
    调用的时候,直接使用content,index即可
    
    //this的绑定优化 都可以写在constructor里面
    onChange={this.handleChange.bind(this)}
    constructor(props){
      super(props);
      ....
      ....
      this.handleChange = this.handleChange.bind(this) //优化地方
    }
    调用的时候直接写:
    this.handleChange
    
    //Ui优化
    当一段的UI太过于庞大的是时候,可以把这段UI放到一个函数方法里面,return 返回出去,同时在使用的地方调用一下即可
    
    //setState键值对优化为异步函数
    this.setState({
      inputValue: e.target.value
    })
    改为
    this.setState(()=>{
       const value = e.target.value  //在异步里面必须要提出来,如果直接须赋值会报错的
       return {
         inputValue:value  //如果将value 改为e.target.value 会报错
       }
    })
    
    //setState中this.state 改为 参数prevState
    handleAdd = () => {
            this.setState((prevState) => {
                return {
                    list: [...prevState.list, prevState.inputValue],
                    inputValue: ''
                }
            });
     };
    
    //if else判断改变为 switch case也可以提高性能
    
    
    3-5 围绕 React 衍生出的思考

    声明式开发
    可以与以其他框架并存
    组件化
    单项数据流(父组件可以向子组件传值,但是子组件一定不能直接去改变这个值)
    视图层框架 (负责视图渲染,但是针对一些大数据和复杂数据的时候,需要交由redux等数据框架来处理)
    函数式编程 (便于自动化测试)

    第4章 React高级内容

    4-1 React developer tools 安装及使用

    打开chrome浏览器,并且打开chrome应用商店,搜索React Developer Tools 添加即可
    React Developer Tools 地址
    百度网盘下载地址

    4-2 PropTypes 与 DefaultProps 的应用

    创建好的组件,最好给组件定义属性类型(string、number、boolean等等) 和创建默认值。就拿上面todoList这个例子来说吧。
    PropTypes
    父组件(TodoList)向子组件(TodoItem)传递了content(字符串)、index(数字)、deleteItem(方法),那么如果我们在子组件中声明了这些类型,就可以避免一些不别要的麻烦,如果父组件传递过来的属性值不是我们想要的,那么我就可以告诉浏览器我需要什么类型的值
    DefaultProps
    可以当父组件没有给子组件传递任何值的时候,通过DefaultProps给子组件初始化一些数据(即默认值)
    详细代码见 3-1 使用React编写TodoList功能
    了解更多PropTypes知识链接地址

    4-3 props,state 与 render 函数的关系

    1.当组件的state或者props发生改变时,render函数就会重新执行
    2.当父组件的render函数被重新执行时,它的子组件的render函数都将被重新执行
    eg:
    在render函数的后面进行打印
    打印父组件console.log('parent-render')
    打印子组件console.log('child1-render')
    更改父组件的state值
    你会发现console里面 parent-render 和 child1-render 都被打印出来了

    4-4 React 中的虚拟DOM

    1.state 数据
    2.JSX 模板
    3.数据+模板 结合,生成真实DOM,来显示
    4.state 发生改变
    5.数据+模板 结合,生成真实DOM,替换原始DOM

    缺陷:
    第一次生成一个完整的DOM片段
    第二次生成一个完整的DOM片段
    第二次的DOM替换第一次的DOM,非常消耗性能

    解决??
    1.state 数据
    2.JSX 模板
    3.数据+模板 结合,生成真实DOM,来显示
    4.state 发生改变
    5.数据+模板 结合,生成真实DOM,并不直接替换原始DOM
    6.新的DOM(documentFragment)和原始的DOM,做对比,找差异
    7.找出input框发生改变的部分
    8.只用新的DOM中的input元素,替换老的DOM中input的元素

    缺陷:
    性能的提升并不明显

    接着解决:
    1.state 数据
    2.JSX 模板
    3.生成虚拟DOM(其实就是一个js对象,用它来描述真实DOM)(损耗了性能但是极小)

    ['div',{id:'abc'},['span',{},'hello world']]
    

    4.用虚拟DOM的结构生成真实DOM,来显示

    <div id="abc"><span>hello world</span></div>
    

    5.state 发生改变
    6.数据+模板 结合,生成新的虚拟DOM (极大的提升了性能)

    ['div',{id:'abc'},['span',{},'bye bye']]
    

    7.比较原始虚拟DOM和生成新的虚拟DOM,找到区别是span的内容(极大的提升了性能)
    8.直接操作DOM,改变span的内容

    总结如下:
    虚拟DOM其实就是js对象,那为什么采用虚拟dom会提高性能呢?因为他们比对的是js对象,而不是真的DOM,从而极大的提升了性能

    4-5 深入了解虚拟DOM
    JSX(模板) => 虚拟DOM(js对象) =>真实的DOM
    render() {
      return <div>item</div>
    }
    等价于
    render() {
      return React.createElement('div',{},'item')
    }
    

    虚拟DOM有点:
    1.性能提升了
    2.它是的跨度应用得以实现 React Native
    因为数据+模板生成的是虚拟DOM,
    1)、如果是在网页中,那么再有虚拟DOM(JS对象)生成真实的DOM,浏览器可以得以识别,所以网页中可以得到应用
    2)、如果在App中,那么再用虚拟DOM(JS对象)不去生成真实的DOM,而去生成原生的组件,那么在App中也就可以得到应用

    4-6 虚拟 DOM 中的 Diff 算法

    虚拟DOM什么时候回发生比对呢?
    答:那就是当state值发生改变的时候,虚拟DOM才会开始进行比对

    为什么setState设计成为异步的?
    答:我们知道,当state发生改变或者props(即父组件中的state发生改变)时,也就是当我们调用setState方法的时候,state值发生改变,虚拟DOM开始进行比较。
    那么如果连续3次调用setState方法的时候,变更3组数据(我们此时会想react会进行三次虚拟DOM比对,三次渲染页面),其实react会把短时间内连续调用setState方法合并为一个setState,只去做一次虚拟DOM的比对,然后更新一次DOM,这样就可以省去额外两次DOM比对带来的性能消耗,所以把setState设计成为异步的

    react中虚拟DOM比对采用的是diff算法:其实同层比对,key值比对


    同层比对 key值比对

    所以列表循环中不要用index作为key值的,在diff算法进行比较时候,会导致key变更,而产生一些性能上的问题
    因为index(索引值有时候会变更)会导致key值不稳定,
    eg:
    a 0 b 1 c 2
    当删除a
    b 0 c 1
    使用一个稳定的值作为key才是我们首先要考虑的
    eg:
    a a b b c c
    当输出a
    b b c c

    4-7 React 中 ref 的使用

    ref是帮助我们react直接获得dom元素,一般情况下我们尽量不要使用ref
    eg:

    <input
       id="inputArea"
       type="text"
       value={this.state.inputValue}
       onChange={this.handleInputChange}
       ref={(input) => this.input = input}  //ref 使用
    />
     handleInputChange = (e) => {
            //const value = e.target.value;
            const value = this.input.value; //通过ref 来获取input的value值
            this.setState(() => {
                return {
                    inputValue: value
                }
            })
    };
    

    tip:
    当我们在试用ref获取dom元素的时候,有时候会出现数据不对,或者少一步操作现象,那么因为setState是异步函数,解决这个问题就是把ref相关操作的内容放到setState回调函数中进行操作

    <ul ref={(ul)=>this.ul = ul}>
      <div>hello</div>
    </ul>
    
    this.setState(() => {
      return {
        inputValue: value
      }
    },() => {
      //回调成功后,在这进行ref相关操作 this.ul.querySelectorAll('div').length
    })
    

    在React v16.3 版本中引入的 React.createRef()方法

    //使用方法
    import React, {Component} from 'react';
    
    class Products extends Component {
      constructor(props) {
        super(props);
        this.myRef = React.createRef(); //这个地方
      }
    
      handleChangeInputValue = () => {
        console.log(this.myRef.current.value) //取值的时候借助current属性
      };
    
      render() {
        return (
          <div>
            <input type="text" ref={this.myRef} onChange={this.handleChangeInputValue}/>
          </div>
        )
      }
    }
    
    export default Products
    

    Ref的适合使用的几个场景:

    • 处理焦点、文本选择或媒体控制。
    this.textInput.current.focus();
    
    • 触发强制动画。
    • 集成第三方 DOM 库
    4-8 React 的生命周期函数

    生命周期函数:
    某一时刻自动调用执行的函数。

    //虽然不是react的生命周期函数,但是有这个功能,初始化数据就放在constructor里面进行操作的
    1.初始化
    constructor(props){
      super(props);
    }
    
    2.挂载的生命周期函数(Mounting)
    //当组件即将要被挂载到页面的时候,会被执行
    componentWillMount(){}
    //render
    render(){}
    //当组件被挂载到页面后,会被执行
    componentDidMount(){}
    
    3.数据发生改变更新的生命周期函数(Updating)
    //当组件被更新之前,会被执行
    shouldComponentUpdate(nextProps,nextState){
      const {list} = this.props;
       if(nextProps.list !== list){
            return true
       }else{
          return false
       }
    }
    
    //当组件被更新之前,会被执行,但是在shouldComponentUpdate后面
    //如果shouldComponentUpdate 返回true 它才执行
    //如果返回 false 它不会被执行
    componentWillUpdate(){}
    
    //当组件被更新之后,会被执行
    componentDidUpdate(){}
    
    //一个组件要从父组件接受参数
    //如果这个组件第一次存在于父组件中,不会被执行
    //如果这个组件之前已经存在父组件中,才会被执行
    componentWillReceiveProps(nextProps){}
    
    4.组件从页面卸载的生命周期函数
    //当这个组件将要被从页面剔除出去的时候,会被执行
    componentWillUnmount(){}
    
    
    react生命周期函数
    4-9 React 生命周期函数的使用场景

    1.生命周期使用场景

    //针对组件的优化的时候,可以使用shouldComponentUpdate,当组件当中的属性值发生改变的是去做更新,没有改变则不去更新,减少组件被重复多次渲染的频率,减少性能的消耗
    shouldComponentUpdate(nextProps, nextState) {
            const {content} = this.props;
            if (nextProps.content !== content) {
                return true
            } else {
                return false
            }
        }
    
    //ajax接口请求 放在componentDidMount()里面
    import axios from 'axios'
    componentDidMount(){
      axios.get('api')
         .then((res)=>{
            console.log('success')
          })
        .catch(()=>{
            console.log('error')
        })
    }
    
    

    2.性能优化
    1)、shouldComponentUpdate
    2)、setState采用的是异步回调的方式,提高了性能
    3)、虚拟DOM中的同层比对和key值比对,也极大的提高了性能

    4-10 使用Charles实现本地数据mock

    charles 下载安装

    //桌面创建一个todoList.json文件
    ["react","vue","angular"]
    
    //设置charles
    打开 charles ---> 菜单栏tools ---> Map Local Setting
    
    //请求接口
    componentDidMount() {
            axios.get('/api/todoList')
                .then((res) => {
                    this.setState(() => {
                        return {
                            list: [...res.data]
                        }
                    })
                })
                .catch(() => {
                    console.log('error')
                })
    }
    
    Map Local Setting设置
    4-11 React 中实现 CSS 过渡动画

    css过度动画主要还是css3知识点的理解和认识,比如:transition(all 1s ease-in)、animation(show-item 1s ease-in forwards)、@keyframes等等
    还就就是借助第三方库react-transition-group等等

    //state
     constructor(props) {
            super(props);
            this.state = {
                show: true
            };
    
            this.handleToggle = this.handleToggle.bind(this);
    }
    
    //JSX
    <h3>动画部分</h3>
    <div className={this.state.show ? 'show' : 'hide'}>hello</div>
    <button onClick={this.handleToggle}>toggle</button>
    
    //绑定事件
    handleToggle() {
            this.setState((prevState) => {
                return {
                    show: prevState.show === true ? false : true
                }
            })
    }
    
    //style
    .show {
        /*opacity: 1;*/
        /*transition: all 1s ease-in;*/
    
        animation: show-item 2s ease-in forwards;
    }
    
    .hide {
        /*opacity: 0;*/
        /*transition: all 1s ease-in;*/
        animation: hide-item 2s ease-in forwards;
    }
    
    @keyframes show-item {
        0% {
            opacity: 0;
            color: red;
        }
        50% {
            opacity: 0.5;
            color: green;
        }
        100% {
            opacity: 1;
            color: blue;
        }
    }
    
    @keyframes hide-item {
        0% {
            opacity: 1;
            color: red;
        }
        50% {
            opacity: 0.5;
            color: green;
        }
        100% {
            opacity: 0;
            color: blue;
        }
    }
    
    4-12 使用 react-transition-group 实现动画
    import  { CSSTransition } from 'react-transition-group';
    <CSSTransition
       in={this.state.show}
       timeout={1000}
       classNames="fade"
       unmountOnExit
       onEntered={(el)=>{el.style.color='blue'}}
       appear={true}
    >
       <div>hello</div>
    </CSSTransition>
    
    //css
    
    /*CSSTransition*/
    .fade-enter,fade-appear{
        opacity: 0;
    }
    .fade-enter-active,fade-appear-active{
        opacity: 1;
        transition: opacity 1s ease-in;
    }
    .fade-enter-done{
        opacity: 1;
    }
    
    .fade-exit{
        opacity: 1;
    }
    .fade-exit-active{
        opacity: 1;
        transition: opacity 1s ease-in;
    }
    .fade-exit-done{
        opacity: 0;
    }
    

    当我们想一个数组都拥有动画的时候,我们会用react-transition-group里面的TransitionGroup
    了解更多链接地址

    5-1 Redux 入门

    -1 Redux 概念简述

    react是一个视图层框架
    那么,redux就是一个数据层框架
    Redux = Reducer + Flux


    image.png
    5-2 Redux 的工作流程
    Redux 的工作流程

    React Component:借书者
    Action Creators: 我要借本三国演义这句话
    Store: 图书馆管理员
    Reducer:记录本

    React Component:我想要借本三国演义
    Action Creators:那我给你写封信(dispatch 发号了一个action指令),我把这个指令告诉了Store
    Store:我知道了,但是我不知道这本书放到哪了,我帮你问问Reducers
    Reducer:我给你查查,查到了,在这呢。带着三国演义这本书(newState)给了Store,Store记录下了借书的内容信息,并这本书最终给了React Components

    5-3 使用 Antd 实现 TodoList 页面布局
    //1.安装antd并且引入antd样式文件
    npm install antd -S
    
    //2.todoList.js
    import React, {Component} from 'react';
    import {Input, Button, List} from 'antd';
    
    import 'antd/dist/antd.css';
    
    import store from './store'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
            this.handleInputValue = this.handleInputValue.bind(this);
            this.state = store.getState();
        }
    
        render() {
            return (
                <div style={{marginTop: '10px', marginLeft: '10px'}}>
                    <Input
                        value={this.state.inputValue}
                        placeholder="todo info"
                        style={{width: '300px', marginRight: '10px'}}
                    />
                    <Button type="primary" >提交</Button>
                    <List
                        style={{marginTop: '10px', width: '300px'}}
                        bordered
                        dataSource={this.state.list}
                        renderItem={item => (<List.Item>{item}</List.Item>)}
                    ></List>
                </div>
            )
        }
    
        handleInputValue(e) {
            const action = {
                type: 'change_input_value',
                value: e.target.value
            };
            store.dispatch(action)
        }
    
    }
    
    export default TodoList;
    
    5-4 创建 redux 中的 store
    //在src目录下创建store目录,并在store目录下创建index.js(图书馆管理员)和reducer.js(记录本)
    
    //index.js
    import {createStore} from 'redux';
    import reducer from './reducer';
    const store = createStore(reducer);
    export default store
    
    //reducer.js
    const defaultState = {
        inputValue: '123',
        list: [1, 2]
    };
    
    export default (state = defaultState, action) => {
        return state
    }
    
    //TodoList.js
    import store from './store'
    constructor(props) {
         super(props);
         this.state = store.getState();
         console.log(store.getState()) //打印出来的数据是{inputValue: "123",Array(2)}
    }
    

    创建图书馆store 并打通 store和reducer之间的桥梁,
    然后react component 通过 store.getState()方法拿到 reducer里面的数据了

    5-5 Action 和 Reducer 的编写

    1.Redux DevTools安装和配置
    去chrome应用商店 安装 Redux DevTools这个浏览器插件
    Redux DevTools下载链接地址
    安装完了以后,发现用chrome浏览器打开控制台,点击redux标签,并没有显示任何内容,那是因为需要我们在store文件中写一段代码。
    配置相关信息的地址,打开github,搜索 redux-devtools-extension,就能查看相关的配置信息了

    import {createStore} from 'redux';
    import reducer from './reducer';
    
    const store = createStore(
        reducer, 
        window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() //这段代码,为了方便浏览器能捕获到redux数据
    );
    
    export default store
    

    2.redux的工作流程原理演示(todoList这个例子来说)
    根据上面的redux工作流,咱们来说。
    如果我们想要更改React Component(input里面的值)
    那么我们需要创建一个Action creators,并且定义一个action对象,通过store.dispatch方法传递给store

    handleInputValue(e) {
           //定义一个action
            const action = {
                type: 'change_input_value',
                value: e.target.value
            };
          // 通过store.dispatch方法,把action传递给store
            store.dispatch(action)
    }
    

    当store接受到Action creators发送过来的action的时候,它说我需要我的秘书reducer帮我查询一下,并且帮我处理一下。
    于是乎reducer就就收了两个参数,第一个是state(定义初始化的旧数据),第二个就是传递过来的action(一个type名称,一个是传递过来新的inputValue值),
    reducer说:我不能直接去更改state里面的值,我需要把state值通过JSON的parse和stringify进行数据深层拷贝生成newState。那么在对这个newState进行数据的处理,最后把处理好的数据再return 回去
    store拿到 reducer处理好的新数据后,
    再通过自己的store.getState()方法,去拿到reducer的最新数据
    再通过自己的store.subscribe()方法,去监测store里面的数据变化,
    最后通过this.setState()方法,把最新的数据渲染到页面上去

    通过第一方法专门是获得最新数据的store.subscribe(this.handleStore);

    //reducer.js
    const defaultState = {
        inputValue: '123',
        list: [1, 2]
    };
    
    export default (state = defaultState, action) => {
        console.log(state, action);
        if (action.type === 'change_input_value') {
            const newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState
        }
    
        if (action.type === 'add_list_item') {
            const newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState
        }
        return state
    }
    
    //TodoList.js
    import React, {Component} from 'react';
    import {Input, Button, List} from 'antd';
    
    import 'antd/dist/antd.css';
    
    import store from './store'
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
            this.handleInputValue = this.handleInputValue.bind(this);
            this.handleAdd = this.handleAdd.bind(this);
            this.handleStore = this.handleStore.bind(this);
            this.state = store.getState(); 
    
            store.subscribe(this.handleStore);//检测handleStore方面里面数据的变化
        }
    
        render() {
            return (
                <div style={{marginTop: '10px', marginLeft: '10px'}}>
                    <Input
                        value={this.state.inputValue}
                        placeholder="todo info"
                        style={{width: '300px', marginRight: '10px'}}
                        onChange={this.handleInputValue}
                    />
                    <Button type="primary" onClick={this.handleAdd}>提交</Button>
                    <List
                        style={{marginTop: '10px', width: '300px'}}
                        bordered
                        dataSource={this.state.list}
                        renderItem={item => (<List.Item>{item}</List.Item>)}
                    ></List>
                </div>
            )
        }
    
        handleInputValue(e) {
            //定义一个action 把这个type:干什么事情的名字传递过去,并且发生更改的信息传递过去
            const action = {
                type: 'change_input_value',
                value: e.target.value
            };
            store.dispatch(action)
        }
    
        handleAdd() {
            const action = {
                type: 'add_list_item'
            };
            store.dispatch(action)
        }
    
        handleStore() {
            //把变更后新的数据,重新放入到state中,然后去渲染页面
            this.setState(store.getState());
        }
    
    }
    
    export default TodoList;
    
    5-6 ActionTypes 的拆分

    为什么要把action里面的type 拆分到一个文件里面呢?
    第一当我们把type值拼写错误的时候,不好找错
    第二我们需要调用相同的内容,写两次
    所以我们在store文件下面创建了一个actionType.js

    5-7 使用 actionCreator 统一创建 action

    为什么要把把组件中的action通过方法的形式拆分出去呢?
    第一为了方便action的统一管理
    第二为了减少React Component 代码的繁琐

    //在store文件下创建actionCreators.js
    import {CHANGE_INPUT_VALUE, ADD_LIST_ITEM, DELETE_LIST_ITEM} from './actionTypes';
    
    export const getChangeInputValue = (value) => ({
        type: CHANGE_INPUT_VALUE,
        value
    });
    
    export const getAddListItem = () => ({
        type: ADD_LIST_ITEM
    });
    
    export const getDeleteListItem = (index) => ({
        type: DELETE_LIST_ITEM,
        index
    });
    
    //TodoList.js
    import {getChangeInputValue, getAddListItem, getDeleteListItem} from './store/actionCreators';
    handleInputValue(e) {
            const action = getChangeInputValue(e.target.value);
            store.dispatch(action)
    }
    
    handleAdd() {
            const action = getAddListItem();
            store.dispatch(action)
    }
    
    handleDeleteItem(index) {
            const action = getDeleteListItem(index);
            store.dispatch(action);
    }
    
    5-8 Redux 知识点复习补充

    1.Redux在设计和使用的三个原则
    1).store是唯一的
    2).只有store能改变自己的内容
    3).reducer必须是个纯函数(给个固定的输入,就一定会有固定的输出,且不会有任何副作用)
    所以里面不能有异步操作(ajax),不能有时间的操作new Date()
    2.redux中核心的API
    1).createStore 帮助我们创建一个store
    2).store.dispatch() 帮助我们派发一个action
    3).store.getState() 帮助我们获得store当中所有的数据
    1).store.subscribe() 帮助我们订阅(监测)store当中数据的改变

    第6章 Redux进阶

    6-1 UI组件和容器组件

    1.UI组件
    UI组件负责页面的渲染
    eg:

    import React, {Component} from 'react'
    import {Button, Input,List} from "antd";
    
    class TodoListUi extends Component{
        render(){
            return (
                <div style={{marginTop: '10px', marginLeft: '10px'}}>
                    <Input
                        value={this.props.inputValue}
                        placeholder="todo info"
                        style={{width: '300px', marginRight: '10px'}}
                        onChange={this.props.handleInputValue}
                    />
                    <Button type="primary" onClick={this.props.handleAdd}>提交</Button>
                    <List
                        style={{marginTop: '10px', width: '300px'}}
                        bordered
                        dataSource={this.props.list}
                        renderItem={(item, index) => (
                            <List.Item onClick={() => this.props.handleDeleteItem(index)}>{item}</List.Item>)}
                    ></List>
                </div>
            )
        }
    }
    
    export default  TodoListUi;
    

    2.容器组件
    它不管你的UI长成什么样子,它只负责页面的业务逻辑

    import React, {Component} from 'react';
    import TodoListUi from './TodoListUi';
    
    import 'antd/dist/antd.css';
    
    import store from './store'
    import {getChangeInputValue, getAddListItem, getDeleteListItem} from './store/actionCreators';
    
    class TodoList extends Component {
        constructor(props) {
            super(props);
            this.handleInputValue = this.handleInputValue.bind(this);
            this.handleAdd = this.handleAdd.bind(this);
            this.handleDeleteItem = this.handleDeleteItem.bind(this);
            this.handleStore = this.handleStore.bind(this);
            this.state = store.getState();
    
            store.subscribe(this.handleStore);
        }
    
        render() {
            return (
                <TodoListUi
                    inputValue={this.state.inputValue}
                    list={this.state.list}
                    handleInputValue={this.handleInputValue}
                    handleAdd={this.handleAdd}
                    handleDeleteItem={this.handleDeleteItem}
                />
            )
        }
    
        handleInputValue(e) {
            const action = getChangeInputValue(e.target.value);
            store.dispatch(action)
        }
    
        handleAdd() {
            const action = getAddListItem();
            store.dispatch(action)
        }
    
        handleDeleteItem(index) {
            const action = getDeleteListItem(index);
            store.dispatch(action);
        }
    
        handleStore() {
            this.setState(store.getState());
        }
    
    }
    
    export default TodoList;
    
    6-2 无状态组件

    当一个组件只有render函数的时候,那么就可以把这个组件改写为无状态组件
    优势:性能比较高
    无状态组件是一个函数
    而一般组件是声明的一个类,这个类里面还有一些生命周期函数,所以执行起来不仅需=要执行类还要执行render
    那么什么时候去用无状态组件呢?
    当我们定义UI组件的时候,因为没有业务逻辑,只有一个render,所以一般在情况下,在UI组件中我们使用无状态组件比较多一些

    //无状态组件
    import React from 'react'
    import {Button, Input, List} from "antd";
    
    const TodoListUi = (props) => {
        return (
            <div style={{marginTop: '10px', marginLeft: '10px'}}>
                <Input
                    value={props.inputValue}
                    placeholder="todo info"
                    style={{width: '300px', marginRight: '10px'}}
                    onChange={props.handleInputValue}
                />
                <Button type="primary" onClick={props.handleAdd}>提交</Button>
                <List
                    style={{marginTop: '10px', width: '300px'}}
                    bordered
                    dataSource={props.list}
                    renderItem={(item, index) => (
                        <List.Item onClick={() => props.handleDeleteItem(index)}>{item}</List.Item>)}
                ></List>
            </div>
        )
    };
    
    export default TodoListUi;
    
    6-3 Redux 中发送异步请求获取数据
    //在actionTypes.js中创建一个变量
    export const INIT_LIST_DATA = 'init_list_data';
    
    //在actionCreators.js中创建一个action
    export const getTodoListData = (data) => ({
        type: INIT_LIST_DATA,
        data
    });
    
    /TodoList.js
    import axios from 'axios';
    import {getTodoListData} from './store/actionCreators';
    componentDidMount() {
            axios.get('/api/todoList')
                .then((res) => {
                    const data = res.data;
                    const action = getTodoListData(data);
                    store.dispatch(action)
                })
    }
    
    //reducer.js
    import {INIT_LIST_DATA} from './actionTypes.js'
    const defaultState = {
      inputValue:'',
      list:[]
    }
    export default (state = defaultState, action) =>{
         if(action.type === INIT_LIST_DATA){
          const newState = JSON.parse(JSON.stringify(state))
          newState.list = action.data
          return newState
        }
        return state
    }
    
    6-4 使用Redux-thunk 中间件实现ajax数据请求

    1.Redux-thunk 安装 以及 redux-Devtools的配置

    //安装
    npm install redux-thunk -S
    
    //redux-Devtools的配置 store文件下的index.js
    import {createStore, applyMiddleware, compose} from 'redux';
    import reducer from './reducer';
    import thunk from 'redux-thunk';
    
    const composeEnhancers =
        window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
    
    const enhancer = composeEnhancers(
        applyMiddleware(thunk),
    );
    
    const store = createStore(reducer, enhancer);
    
    export default store
    

    配置参考地址链接:redux-devtools-extension

    2.redux-thunk 在程序中的应用
    为什么使用redux-thunk这个redux中间件?
    第一:可以把数据操作和数据请求操作从React Component中搬到ActionCreators.js里面,不会是的组件显得那么拥堵
    第二:便于后期的单元测试

    //actionCreators.js中的修改 
    import axios from 'axios';
    export const getTodoListDataAction = (data) => ({
        type: INIT_LIST_DATA,
        data
    });
    
    export const getListData = () => {
        //redux-thunk 返回action是一个函数的时候,且放到了action里面进行操作的
        return (dispatch) => {
            axios.get('/api/todoList')
                .then((res) => {
                    const data = res.data;
                    const action = getTodoListDataAction(data);
                    dispatch(action)
                })
        }
    };
    
    //TodoList.js中的修改
    import { getListData } from './store/actionCreators';
    componentDidMount() {
            const action = getListData();
            store.dispatch(action);
    }
    
    6-5 什么是Redux的中间件
    redux数据工作流

    redux-thunk 其实是对store.dispatch(action)方法的一个封装和升级,是把异步请求的操作放到了action当中进行操作。
    在没有使用redux-thunk的时候,定义的action是一个对象
    使用redux-thunk之后,定义的action不仅可以是对象,而且还可以可以是一个函数

    其他redux中间件:

    redux-logger:可以记录action每次派发的日志
    redux-saga:也是解决react中异步的一个中间件,单独的把异步的操作放到一个文件中进行操作

    6-8 Redux-saga中间件入门

    1.redux-sage的安装和配置

    //安装
    npm install redux-saga -S
    
    //配置是在 store文件下面的index.js中
    import {createStore, applyMiddleware, compose} from 'redux';
    import reducer from './reducer';
    // import thunk from 'redux-thunk';
    import createSagaMiddleware from 'redux-saga';
    import todoSaga from './sagas'
    
    const sagaMiddleware = createSagaMiddleware();
    
    
    
    const composeEnhancers =
        window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
    
    const enhancer = composeEnhancers(
        applyMiddleware(sagaMiddleware),
    );
    
    const store = createStore(reducer, enhancer);
    sagaMiddleware.run(todoSaga);
    
    export default store
    
    //sagas.js
    
    

    配置参考地址链接:redux-saga

    2.redux-saga在程序中的应用

    //TodoList.js
    componentDidMount() {
            const action = getTodoListData();
            store.dispatch(action);
    }
    
    //actionTypes.js
    export const INIT_LIST_DATA = 'init_list_data';
    
    //actionCreators.js
    import {INIT_LIST_DATA} from './actionTypes';
    export const getTodoListData = (data) => ({
        type: INIT_LIST_DATA,
        data
    });
    
    //reducer.js
    import {INIT_LIST_DATA} from './actionTypes';
    const defaultState = {
        inputValue: '',
        list: []
    };
    export default (state = defaultState, action) => {
        // console.log(state, action);
        if (action.type === INIT_LIST_DATA) {
            const newState = JSON.parse(JSON.stringify(state));
            newState.list = action.data;
            return newState
        }
        return state
    }
    
    //sagas.js
    import {takeEvery, put} from 'redux-saga/effects';
    import {INIT_LIST_DATA} from './actionTypes';
    import {getTodoListData} from './actionCreators';
    import axios from 'axios';
    
    function* getListData() {
        try {
            const res = yield axios.get('/api/todoList');
            const action = getTodoListData(res.data);
            yield put(action)
        } catch(e) {
            console.log('todoList 网络异常')
        }
    }
    
    function* todoSaga() {
        yield takeEvery(INIT_LIST_DATA, getListData);
    }
    
    export default todoSaga;
    

    总结:
    1).ajax请求
    不采用Promise那种形式了(.then),而是通过yield 来等待返回的结果
    2).接受或者监听Action的
    通过的takeEvery,检测到变量名称,触发一个generator函数
    3).派发请求
    不采用store.dispatch(), 而是通过的是put()
    4).出了takeEvery、put方法还有(takeLatest、call等等多种API)

    6-9 如何使用 React-redux

    react-redux核心API有哪些?
    1).Provider:就是一个连接器的组件,因为Provider和store做了关联,所以Provider这些内部的组件都可以获取到store里面的数据内容了

    //安装react-redux
    npm install react-redux -S
    
    //使用 在src文件下面的index.js文件进行编写
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider }  from 'react-redux'
    import TodoList from './TodoList'
    import store from './store'
    
    const App = (
          <Provider store={store}>
            <TodoList />
          </Provider>
    )
    
    ReactDOM.render(App,document.getElementById('root'))
    

    2).connect: 是React Component调用react-redux的connect方法,使得组件和store关联起来,并且能对state进行设置和修改

    import React,{ Component } from 'react'
    import {connect} from 'react-redux;
    
    class TodoList extends Component {
        render() {
          return (
              <div>
                     <div>
                        <input 
                            value={this.props.inputValue} 
                            onChange={this.props.handleInputChange}
                        />
                        <button>提交</button>
                    </div>
                    <div>
                        <ul><li>hello</li></ul>
                    </div>
              </div>
          )
        }
    }
    const mapStateToProps = (state) => {
        return {
            inputValue: state.inputValue
        }
    }
    
    //store.dispatch,props
    mapDispatchToProps = (dispatch) => {
          return {
              handleInputChange(e) {
                  const action = {
                      type:'change_input_value',
                      value: e.target.value
                   }
                  dispatch(action)
              }
          }
    }
    
    export default connect(mapStateToProps,mapDispatchToProps)(TodoList)
    

    6-12 最终TodoList功能
    通过react官网提供的脚手架工具(creat-react-app)来搭建项目
    1).采用了react全家桶:
    react
    react-dom
    react-redux
    redux
    redux-thunk
    2).ajax请求
    axios
    3).项目目录


    项目目录

    4).代码展示

    //src文件下的 index.js
    import React from 'react'
    import ReactDOM from 'react-dom';
    import {Provider} from 'react-redux';
    import TodoList from './TodoList'
    import store from './store'
    
    const App = (
        <Provider store={store}>
            <TodoList/>
        </Provider>
    )
    
    ReactDOM.render(App, document.getElementById('root'));
    
    //TodoList.js
    import React, {Component} from 'react'
    import {connect} from "react-redux";
    import {getInputValueAction, getHandleClickAction, getDeleteItemAction, getListDataApi} from './store/actionCreators'
    
    class TodoList extends Component {
        render() {
            const {inputValue, list, handleInputChange, handleClick, handleDelete} = this.props;
            return (
                <div>
                    <div>
                        <input
                            type="text"
                            value={inputValue}
                            onChange={handleInputChange}
                        />
                        <button onClick={handleClick}>提交</button>
                    </div>
                    <div>
                        <ul>
                            {
                                list.map((item, index) => (
                                    <li key={index} onClick={() => handleDelete(index)}>{item}</li>
                                ))
                            }
                        </ul>
                    </div>
                </div>
            )
        }
    
        componentDidMount() {
            this.props.getListData()
        }
    }
    
    const mapStateToProps = (state) => {
        return {
            inputValue: state.inputValue,
            list: state.list
        }
    };
    
    const mapDispatchToProps = (dispatch) => {
        return {
            handleInputChange(e) {
                const action = getInputValueAction(e.target.value);
                dispatch(action)
            },
            handleClick() {
                const action = getHandleClickAction();
                dispatch(action)
            },
            handleDelete(index) {
                const action = getDeleteItemAction(index);
                dispatch(action)
            },
            getListData() {
                const action = getListDataApi();
                dispatch(action);
            }
        }
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
    
    //store 文件下的index.js
    import {createStore, applyMiddleware, compose} from 'redux';
    import thunk from 'redux-thunk';
    import reducer from './reducer'
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
    
    const enhancer = composeEnhancers(applyMiddleware(thunk));
    const store = createStore(reducer, enhancer);
    
    export default store;
    
    //store 文件下的reducer.js
    import {CHANGE_INPUT_VALUE, ADD_ITEM, DELETE_ITEM, GET_LIST_DATA} from './actionTypes';
    
    const defaultState = {
        inputValue: '',
        list: []
    };
    export default (state = defaultState, action) => {
        if (action.type === CHANGE_INPUT_VALUE) {
            const newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState
        }
        if (action.type === ADD_ITEM) {
            const newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState
        }
        if (action.type === DELETE_ITEM) {
            const newState = JSON.parse(JSON.stringify(state));
            newState.list.splice(action.index, 1);
            return newState
        }
        if (action.type === GET_LIST_DATA) {
            const newState = JSON.parse(JSON.stringify(state));
            newState.list = action.data;
            return newState
        }
        return state
    }
    
    //store 文件下的actionTypes.js
    export const CHANGE_INPUT_VALUE = 'change_input_value';
    export const ADD_ITEM = 'add_item';
    export const DELETE_ITEM = 'delete_item';
    export const GET_LIST_DATA = 'get_list_data';
    
    //store 文件下的actionCreators.js
    import axios from 'axios';
    import {CHANGE_INPUT_VALUE, ADD_ITEM, DELETE_ITEM, GET_LIST_DATA} from './actionTypes';
    
    export const getInputValueAction = (value) => ({
        type: CHANGE_INPUT_VALUE,
        value
    });
    
    export const getHandleClickAction = () => ({
        type: ADD_ITEM
    });
    
    export const getDeleteItemAction = (index) => ({
        type: DELETE_ITEM,
        index
    });
    
    export const getListDataAction = (data) => ({
        type: GET_LIST_DATA,
        data
    });
    
    export const getListDataApi = () => {
        return (dispatch) => {
            axios.get('/api/todoList')
                .then(res => {
                    const data = res.data;
                    const action = getListDataAction(data);
                    dispatch(action)
                })
                .catch((e) => {
                    console.log('/api/todoList 网络异常')
                })
        }
    };
    

    第7章 项目实战中的一些技巧

    7-1 styled-components的应用

    在写react组件的时候,为了防止样式被污染到,我们可以通过styled-components自定义标签以及样式。

    //1.安装 styled-components
    npm install styled-components -S
    
    //2.初步使用方法,创建一个style.js文件
    import styled from 'styled-components';
    
    export const Nav = styled.div`
      width:1000px;
      margin: 0 auto;
      height: 50px;
      line-height: 50px;
      &.txtColor{
        color:red
      }
    `
    组件中引用
    import {Nav} from './style.js'
    <Nav className="txtColor">
    
    //3.attrs属性
    export const NavItem = styled.a.attrs({
      href: '/'
    })`
      //样式
    `
    export const NavItem = styled.input.attrs({
      placeholder: '搜索'
    })`
      //样式
    `
    
    //4.嵌套的应用
    import { Nav,NavItem} from './style.js'
    <Nav>
      <NavItem className="bg">首页</NavItem>
    </Nav>
    export const Nav = styled.div`
      width:1000px;
      margin: 0 auto;
      height: 50px;
      line-height: 50px;
      &.txtColor{
        color:red
      }
      //嵌套写法
      .bg{
          background: red
      }
    `
    
    //5.全局样式的使用(createGlobalStyle),比如reset.css、iconfont.css等等
    export const GlobalStyle = createGlobalStyle`
    //reset.css内容 或者 iconfont.css 内容等等
    `;
    import React from 'react';
    import ReactDOM from 'react-dom';
    import {Provider} from 'react-redux';
    import Main from './Main';
    import store from './store';
    import {GlobalStyle} from './style';
    import {GlobalStyle2} from './statics/font/iconfont'
    
    const App = (
        <Provider store={store}>
            <GlobalStyle/> //这个地方就可以设置为全局样式了
            <GlobalStyle2/>//这个地方就可以设置为全局样式了
            <Main/>
        </Provider>
    );
    
    ReactDOM.render(App, document.getElementById('root'));
    
    //6.参数传递和获取
    应用场景,当我们都在循环一个列表的数据的时候,需要传递这个img作为它的背景图片
    <Toppic imgUrl = '......png'></Topic>
    <Toppic imgUrl = '......png'></Topic>
    
    import styled from 'styled-components'
    export const Topic = styled.div`
      background:url(${(props)=>props.imgUrl})
    `;
    
    

    上面是styled-components的一些常用的使用方法,要是想学习和了解更多。
    styled-components更多学习和了解地址

    7-2 redux中 combinReducers 的使用

    在开发过程中,我们不可能把所有的reducer放到一个文件里面,那么肯定需要对reducer进行拆分的,但是拆分后的reducer最后我们肯定需要在合并到一起呢,因为在redux在创建store的时候,需要reducer的集合作为入参的。所有合并reducer就诞生了combinReducers

    import { combinReducers } from 'reducer'
    import {reducer as headerReducer} from '../common/header/store'
    import {reducer as footerReducer} from '../common/footer/store'
    ...
    
    const reducer = combinReducers({
      header: headerReducer,
      footer: footerReducer
      ...
    })
    export default reducer
    

    ps: 调用的时候注意了

    const mapState = (state) => {
        return {
            focused: state.header.focused //调用时候,你加上你header或者footer
        }
    };
    
    7-3 store的拆分
    store的拆分

    上面我们写TodoList的demo的是,因为只涉及两个页面,所以不会考虑到store的拆分,但是在我们制作项目的时候,我们就的考虑store的拆分了

    1.最外层的store文件(store总中心):(index.js 和 reducer.js)
    index.js:创建store,并且把store和reducer链接起来,而且配置了redux-devtools可以让我们在chrome里面看到redux的变化
    reducer.js: 把项目中各个地方的reducer通过combinReducers方便合并起来,把合并的最终结果reducer,暴露出去

    //index.js
    import {createStore, applyMiddleware, compose} from "redux";
    import reducer from './reducer';
    import thunk from 'redux-thunk';
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
    
    const enhancer = composeEnhancers(
        applyMiddleware(thunk),
    );
    const store = createStore(reducer, enhancer);
    
    export default store;
    
    //reducer.js
    import {combineReducers} from "redux";
    import {reducer as headerReducer} from '../common/header/store'
    
    const reducer = combineReducers({
        header: headerReducer
    });
    
    export default reducer;
    

    2.组件中的store文件(碎片store,那公共header为例)的拆分:
    index.js: 把下面三个文件整合到一起,并且暴露出去,为了更方便的引用
    constants.js: 定义了一些大写的常量,且暴露出去这些常量
    actionCreators.js: header组件中action的一些方法都放在这个里面,且暴露出去
    reducer.js: header组件中的reducer操作放在这个里面,且暴露出去

    //index.js:
    import reducer from './reducer';
    import * as constants from './constants';
    import * as actionCreators from './actionCreators';
    export {
        reducer, //为了总store中reducer更方便的引入
        constants,//为了actionCreator更方便的引入
        actionCreators//为了组件中更方便的引入
    }
    //总store中reducer引入的时候:import {reducer as headerReducer} from '../common/header/store'
    //actionCreator引入的时候:import {constants} from './index';
    //header组件引入的时候:import {actionCreators} from './store';
    
    //constants.js
    export const INPUT_FOCUSED = 'header/input_focused';
    export const INPUT_BLUR = 'header/input_blur';
    
    //actionCreators.js
    import {constants} from './index';
    export const getInputFocusedAction = () => ({
        type: constants.INPUT_FOCUSED
    });
    export const getInputBlurAction = () => ({
        type: constants.INPUT_BLUR
    });
    
    7-4 使用 Immutable.js 来管理store中的数据

    为了确保原始state不会被修改,导致的一些问题。所以我们引入了Immutable.js来更好维护我们的原始state数据

    //1.安装 immutable 
    npm install immutable -S
    
    //2.使用 immutable中的fromJS 可以把 js对象转变为 immutable对象
    import {constants} from './index';
    import { fromJS } from 'immutable';
    const defaultState = fromJS({
      focused:false
    })
    
    //3.设置  更改state里面的 immutable数据那么就需要.set()方法
    //immutable对象的set方法,会结合之前immutable对象的值和设置的值,返回一个全新的对象
    export default (state = defaultState,action) =>{
        if(action.type === constants.INPUT_FOCUSED)  {
            return state.set('focused',true)
        }
       if(action.type === constants.GET_HEADER_LIST)  {
            //return state.set('list', ).set('totalPage', );
            //改为 state.merge()方法的
             return state.merge({
                    list: action.data,
                    totalPage:action.totalPage
             });
        }
        return state
    }
    
    //4.获取   要使用immutable数据 要通过.get方法
    const mapState = (state) =>{
      return {
        focused: state.header.get('focused')
      }
    }
    
    //5.获取  当需要把immutable对象转化为 普通的js对象时候
    const {list} = this.props
    const newList = list.toJS() //toJS方法的使用
    
    7-5 使用 redux-immutable 统一数据格式

    上一小节说到,我们将state初始化的数据通过immutable这个库变成了immutable对象,确保了state里面数据的稳定性,但是呢,在我们组件去获得immutable的时候:
    focused: state.header.get('focused')中
    state.header是“js对象”
    而后面的.get('focused')则是“immutable对象”
    这样看的有些不统一,那么如何把state.header也变成immutable对象呢?那么我们就去看那个地方设置state.header

    //安装redux-immutable
    npm install redux-immutable -S
    
    //在最外层的reducer.js 文件对跟reducer进行设置
    将
    import {combineReducers} from "redux";
    改为
    import {combineReducers} from "redux-immutable";//redux 改为 redux-immutable
    import {reducer as headerReducer} from '../common/header/store'
    
    const reducer = combineReducers({
        header: headerReducer
    });
    export default reducer;
    
    
    //优化代码
    const mapState = (state) => {
        return {
            focused: state.get(header).get('focused')
        }
    };
    改为 连续调用两次get方法通过getIn方法一次实现
    const mapState = (state) => {
        return {
            focused: state.getIn(['header','focused'])
        }
    };
    

    ps:学习了解更多immutable的知识链接

    7-6 避免无意义的请求发送,提升组件性能

    有些数据并不是我们每次点击就去请求接口,需要我们初次点击的时候,请求一次接口,随后点击就不请求了,那么就要加一些判断限制一下

    const {list} = this.props
    
    //当去请求一个列表的时候,如果初始的数据为0,那么我去请求一次接口
    (list.length === 0) && dispatch(action)
    
    ps:这种只是项目中的一种情况,我们在开发过程中呢,要结合项目开发功能,来写不同的判断来减少或者没必要的接口请求
    
    7-7 什么是路由,如何在React中使用路由功能

    我们使用的是react-router-dom来做路由的

    //安装 react-router-dom
    npm install react-router-dom -S
    
    //使用
    import React, {Component} from 'react';
    import {Provider} from 'react-redux';
    import {BrowserRouter, Route} from 'react-router-dom';
    import store from './store';
    import {GlobalStyle} from './style';
    import {GlobalStyle2} from './statics/font/iconfont'
    import Header from './common/header';
    import Home from './pages/Home';
    import Detail from './pages/Detail';
    
    class App extends Component {
        render() {
            return (
                <Provider store={store}>
                    <GlobalStyle/>
                    <GlobalStyle2/>
                    <Header/>
                    <BrowserRouter>
                        <div>
                            <Route path='/' exact component={Home}></Route>
                            <Route path='/detail' exact component={Detail}></Route>
                        </div>
                    </BrowserRouter>
                </Provider>
            )
        }
    }
    export default App;
    

    2.单页路由的跳转,通过Link 的to属性进行页面的跳转
    单页跳转的好处,减少http请求,请求速度会很快

    import {Link} from 'react-router-dom';
    <Link to='/'>
      <Logo />
    </Link>
    

    3.页面路由参数的传递
    1)、动态路由获取参数
    http://localhost:3000/detail/1

    //配置路由
    <Route path='/detail/:id' exact component={Detail}></Route>
    
    //获取Url上面的1这个参数值
    this.props.match.params.id
    

    2)、非动态路由获取参数
    http://localhost:3000/detail?id=1

    //配置路由
    <Route path='/detail' exact component={Detail}></Route>
    
    //获取Url上面的1这个参数值
    const id = this.props.location.search;   //search: ?id=2
    再对id进行数据的处理才能拿到最终我们想要的值
    
    7-8 PureComponent 的应用
    import React,{Component} from 'react';
    改为 
    import React,{PureComponent} from 'react';
    

    为了考虑react的性能优化,需要我们对变更的数据的进行监测,采用shouldComponentUpdate对组件进行优化,如果这样的话,就需要写很多shouldComponentUpdate的代码,那么react中这个PureComponent组件 就是自动帮助我们做这部分优化功能的。
    注释:如果采用PureComponent这个组件那么,必须保证你的数据是immutable的

    相关文章

      网友评论

        本文标题:React学习总结

        本文链接:https://www.haomeiwen.com/subject/bsjcuqtx.html