React基础

119 阅读13分钟

一、React项目创建

要求: Node >= 14.0.0 and npm >= 5.6

1、项目初始化:

npx create-react-app my-app

cd my-app

npm start

如果初始化时安装过慢,可以尝试设置npm安装镜像,运行以下内容

npm config set registry https://registry.npmmirror.com

npm config get registry // 检测配置是否生效

2、初始化项目文件类型

入口文件:index.js

  1. registerServiceWorker:PWA 离线页面相关内容
  2. index.css 页面样式 删除会出现间距
  3. App.test.js —— 用于自动化测试
  4. App.js文件 —— 页面初始展现的内容是App.js文件里render函数里的内容
  5. ReactDom:使得组件可以挂载到页面上
  6. index.html:页面显示的文件:空内容

注:在react项目里选择查看网页源代码,可以发现浏览器所展示的内容就是public目录下的index.html文件里的内容。

既然源代码是index.html的内容,那我们的react代码为什么会被展示出来呢?

答:是因为我们在App.js文件里,将我们的react代码进行了挂载,直接挂载到了index.html页面中,id为root的div框内,具体代码为:

ReactDOM.render(<App />, document,getElementById('root')) // 将写的所有组件都挂载到root节点上

二、React的基础使用

1、React组件的基本结构

react组件分为两种形式,一种叫做函数组件,一种叫做类组件,两种组件的编写方法以及使用场景都各有不同,其中,类组件的功能更加全面,函数式组件的功能比较单一

类组件

import React from 'react'



class Demo extends React.Component {

    constructor(props) {

        super(props)

        this.state = {

            name: '测试'

        }

    }

    render() {

        return(

            <p>这是类组件的写法</p>

        )

    }

}



export default JSXBaseDemo

类组件的写法类似使用class关键字构建一个类,在类组件中,使用constructor构造器,对整个组件需要使用的变量进行统一的声明,都写在this.state对象里面。

注:这里的this.state的名字不可随意编写

在类组件中,还有render函数,它类似于vue中的template模板,在render函数中,可以return我们需要编写的html代码,return关键字里的代码写法被称为JSX语法,它允许编写html时不需要增加引号,且可以在html代码中插入js变量或者表达式。

注:这里的写法是JSX语法,JSX语法允许js表达式,但不可以写js语句

函数组件

import React from 'react'



function App() {



  return (

    <div className="App">

      11111

    </div>

  );

}



export default App;

相比于类组件,函数组件的写法相对简单,同时也导致了函数组件的一些限制:

  1. 函数组件内部没有构造器
  2. 函数组件内部没有实例——无法使用this
  3. 函数组件内无render函数

函数组件仅仅是输入props输出一个JSX的方法,不在组件内部实现过多的操作。

2、JSX基本语法

基本语法

  1. 插值写法:单花括号包裹需要绑定的变量值或者函数名——不同于vue双花括号的写法

  2. JSX语法允许写js表达式(三元运算符、Array.map等),但是不允许出现js语句(if判断等)

  3. 类名写法:JSX中声明类名需要使用className关键字,class无法识别

  4. 动态类名:同插值写法,单花括号包裹——不同于vue使用:进行绑定

  5. style写法:JSX允许在html代码中编写style,写法同插值写法——不同于vue用“”包裹

  6. 组件加载:JSX需要在头部引入组件后可直接在render函数里使用——不同于vue需要在component里声明

  7. JSX事件:为元素绑定事件,小驼峰写法。例如:点击事件:onClick C必须大写

  8. JSX中没有v-for、v-model、v-if等关键字,需要程序员自己实现

    1. v-for可以使用Array.map函数实现
    2. v-if可以使用if else代替
    3. v-model需要同时绑定value和onChange事件进行实现
import React from 'react'

import './style.css'

import List from '../List'



class JSXBaseDemo extends React.Component {

    constructor(props) {

        super(props)

        this.state = {

            name: '测试',

            imgUrl: 'https://img1.mukewang.com/5a9fc8070001a82402060220-140-140.jpg',

            flag: true

        }

    }

    render() {

        // 获取变量 插值

        const pElem = <p>{this.state.name}</p>

        return pElem



        // // 表达式

        // const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>

        // return exprElem



        // // 子元素

        // const imgElem = <div>

        //     <p>我的头像</p>

        //     <img src="xxxx.png"/>

        //     <img src={this.state.imgUrl}/>

        // </div>

        // return imgElem



        // class

        // <p className={动态类名}></p>

        // const classElem = <p className="title">设置 css class</p>

        // return classElem



        // // style

        // const styleData = { fontSize: '30px',  color: 'blue' }

        // const styleElem = <p style={styleData}>设置 style</p>

        // 静态的style

        // <p style="font-size: 30px"></p>

        // 内联写法,注意 {{ 和 }}

        // const styleElem = <p style={{ fontSize: '30px',  color: 'blue' }}>设置 style</p>

        // return styleElem



        // 原生 html

        // const rawHtml = '<span>富文本内容<i>斜体</i><b>加粗</b></span>'

        // const rawHtmlData = {

        //     __html: rawHtml // 注意,必须是这种格式

        // }

        // const rawHtmlElem = <div>

        //     <p dangerouslySetInnerHTML={rawHtmlData}></p>

        //     {/* 直接写没有富文本 dangerouslySetInnerHTML写了才有*/}

        //     <p>{rawHtml}</p>

        // </div>

        // return rawHtmlElem



        // // 加载组件

        // const componentElem = <div>

        //     <p>JSX 中加载一个组件</p>

        //     <hr/>

        //     <List/>

        // </div>

        // return componentElem

        // // JSX 事件

// const eventList = <p onClick={this.clickHandler}>

//     some text

// </p>

    }

}



export default JSXBaseDemo

React中的event事件并非原生

不同于vue以及原生的js,在React中,触发元素事件的节点与事件挂载的节点并不相同,看起来一样,是因为react封装好了的event事件可以完全替代原生的所有功能。

1. event 是 SyntheticEvent ,模拟出来 DOM 事件所有能力

2. event.nativeEvent 是原生事件对象

3. 所有的事件,都被挂载到 document 上

4. 和 DOM 事件不一样,和 Vue 事件也不一样
import React from 'react'

/* this绑定、事件绑定 event不是原生的*/



class EventDemo extends React.Component {

    constructor(props) {

        super(props)

        this.state = {

            name: 'zhangsan',

            list: [

                {

                    id: 'id-1',

                    title: '标题1'

                },

                {

                    id: 'id-2',

                    title: '标题2'

                },

                {

                    id: 'id-3',

                    title: '标题3'

                }

            ]

        }



        // 修改方法的 this 指向

        this.clickHandler1 = this.clickHandler1.bind(this)

    }

    render() {

        // // this - 使用 bind

        // return <p onClick={this.clickHandler1}>

        //     {this.state.name}

        // </p>



        // // this - 使用静态方法

        // return <p onClick={this.clickHandler2}>

        //     clickHandler2 {this.state.name}

        // </p>



        // event

        // return <a href="https://baidu.com/" onClick={this.clickHandler3}>

        //     click me

        // </a>



        // 传递参数 - 用 bind(this, a, b)

        return <ul>{this.state.list.map((item, index) => {

            return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>

                index {index}; title {item.title}

            </li>

        })}</ul>

    }

    clickHandler1() {

        // console.log('this....', this) // this 默认是 undefined

        this.setState({

            name: 'lisi'

        })

    }

    // 静态方法,this 指向当前实例 不需要声明绑定

    clickHandler2 = () => {

        this.setState({

            name: 'lisi'

        })

    }

    // 获取 event

    clickHandler3 = (event) => {

        event.preventDefault() // 阻止默认行为

        event.stopPropagation() // 阻止冒泡

        console.log('target', event.target) // 指向当前元素,即当前元素触发

        console.log('current target', event.currentTarget) // 指向当前元素,假象!!!



        // 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是 SyntheticEvent 组合事件

        console.log('event', event) // 不是原生的 Event ,原生的 MouseEvent

        console.log('event.__proto__.constructor', event.__proto__.constructor)



        // 原生 event 如下。其 __proto__.constructor 是 MouseEvent

        console.log('nativeEvent', event.nativeEvent)

        console.log('nativeEvent target', event.nativeEvent.target)  // 指向当前元素,即当前元素触发 触发是触发,但是绑定在document上

        console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!! 不同于vue vue触发的节点和绑定的节点相同

    }

    // 传递参数

    clickHandler4(id, title, event) {

        console.log(id, title)

        console.log('event', event) // 最后追加一个参数,即可接收 event

    }

}



export default EventDemo

React中的双向绑定

react中没有双向绑定语法糖,需要手动实现,原理在于绑定value以及change事件,每次修改时同步更新this.state中的值。

<input id="inputName" value={this.state.name} onChange={this.onInputChange}/>
import React from 'react'



class FormDemo extends React.Component {

    constructor(props) {

        super(props)

        this.state = {

            name: '测试',

            info: '个人信息',

            city: 'beijing',

            flag: true,

            gender: 'male'

        }

    }

    render() {



        // // 受控组件(非受控组件 有些没法被state控制) 没有v-model 可以通过控制state的值控制input的值

        return <div>

            <p>{this.state.name}</p>

            <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for (js中的保留字)*/}

            <input id="inputName" value={this.state.name} onChange={this.onInputChange}/>

        </div>



        // textarea - 使用 value

        // return <div>

        //     <textarea>{this.state.info}</textarea> 不可以这么写,vue也不允许

        //     <textarea value={this.state.info} onChange={this.onTextareaChange}/>

        //     <p>{this.state.info}</p>

        // </div>



        // // select - 使用 value

        // return <div>

        //     <select value={this.state.city} onChange={this.onSelectChange}>

        //         <option value="beijing">北京</option>

        //         <option value="shanghai">上海</option>

        //         <option value="shenzhen">深圳</option>

        //     </select>

        //     <p>{this.state.city}</p>

        // </div>



        // // checkbox

        // return <div>

        //     <input type="checkbox" checked={this.state.flag} onChange={this.onCheckboxChange}/>

        //     <p>{this.state.flag.toString()}</p>

        // </div>



        // // radio

        // return <div>

        //     male <input type="radio" name="gender" value="male" checked={this.state.gender === 'male'} onChange={this.onRadioChange}/>

        //     female <input type="radio" name="gender" value="female" checked={this.state.gender === 'female'} onChange={this.onRadioChange}/>

        //     <p>{this.state.gender}</p>

        // </div>



        // 非受控组件

    }

    onInputChange = (e) => {

        this.setState({

            name: e.target.value

        })

    }

    onTextareaChange = (e) => {

        this.setState({

            info: e.target.value

        })

    }

    onSelectChange = (e) => {

        this.setState({

            city: e.target.value

        })

    }

    onCheckboxChange = () => {

        this.setState({

            flag: !this.state.flag

        })

    }

    onRadioChange = (e) => {

        this.setState({

            gender: e.target.value

        })

    }

}



export default FormDemo

React中的父子组件传值

父子组件的通信

1、react里也存在父子组件

2、父子组件通过属性进行值得传递,注意插值的写法

<Input submitTit le={this.onSubmitTitle}/>

<List list={this.state.list}/>

<Footer text={this.state.footerInfo} length={this.state.list.length}/>

3、子组件通过props接受参数

onSubmit = () => {

        // this.props.submitTitle(this.state.title) 

        const { submitTitle } = this.props

        submitTitle(this.state.title) // 'abc'



        this.setState({

            title: ''

        })

    }

完整代码演示:

代码中TodoListDemo作为最外层父组件,引用了List、Input、Footer 三个组件,实现了一个简易todo-list的功能,父向子传值的时候只需要通过属性绑定即可,子组件接受时通过this.props属性名进行接收。

/**

 * @description 演示 props 和事件

 */



import React from 'react'

import PropTypes from 'prop-types' // props类型检查插件



class Input extends React.Component {

    constructor(props) {

        super(props)

        this.state = {

            title: ''

        }

    }

    render() {

        return <div>

            <input value={this.state.title} onChange={this.onTitleChange}/>

            <button onClick={this.onSubmit}>提交</button>

        </div>

    }

    onTitleChange = (e) => {

        this.setState({

            title: e.target.value

        })

    }

    onSubmit = () => {

        // this.props.submitTitle(this.state.title) 

        const { submitTitle } = this.props

        submitTitle(this.state.title) // 'abc'



        this.setState({

            title: ''

        })

    }

}

// props 类型检查

Input.propTypes = {

    submitTitle: PropTypes.func.isRequired

}



class List extends React.Component {

    constructor(props) {

        super(props)

    }

    render() {

        const { list } = this.props



        return <ul>{list.map((item, index) => {

            return <li key={item.id}>

                <span>{item.title}</span>

            </li>

        })}</ul>

    }

}

// props 类型检查

List.propTypes = {

list: PropTypes.arrayOf(PropTypes.object).isRequired

}



class Footer extends React.Component {

    constructor(props) {

        super(props)

    }

    render() {

        return <p>

            {this.props.text}

            {this.props.length}

        </p>

    }

    componentDidUpdate() {

        console.log('footer did update')

    }

    shouldComponentUpdate(nextProps, nextState) {

        if (nextProps.text !== this.props.text

            || nextProps.length !== this.props.length) {

            return true // 可以渲染

        }

        return false // 不重复渲染

    }



    // React 默认:父组件有更新,子组件则无条件也更新!!!

    // 性能优化对于 React 更加重要!

    // SCU 一定要每次都用吗?—— 需要的时候才优化

}



class TodoListDemo extends React.Component {

    constructor(props) {

        super(props)

        // 状态(数据)提升 所有子组件都用这个数据 把数据放在最高的组件里 统一管理

        this.state = {

            list: [

                {

                    id: 'id-1',

                    title: '标题1'

                },

                {

                    id: 'id-2',

                    title: '标题2'

                },

                {

                    id: 'id-3',

                    title: '标题3'

                }

            ],

            footerInfo: '底部文字'

        }

    }

    render() {

        return <div>

            <Input submitTitle={this.onSubmitTitle}/>

            <List list={this.state.list}/>

            <Footer text={this.state.footerInfo} length={this.state.list.length}/>

        </div>

    }

    onSubmitTitle = (title) => {

        this.setState({

            list: this.state.list.concat({

                id: `id-${Date.now()}`,

                title

            })

        })

    }

}





export default TodoListDemo

3、setState是什么?

刚刚的JSX语法中,我们说到要把整个组件中需要用到的变量都统一写在this.state中,引用时可以使用thia.state.变量名即可引用,那我们应该如何修改已经声明的值呢?

注:react中,如果想改变state中的值,不可直接修改,要使用统一方法this.setState()方法

setState()方法有三个特性:

不可变值:操作state的时候不可直接改变其值

数组

  1. 不能提前对state的值进行修改,会有错误——shouldComponentUpdate
  2. 复杂操作先生成副本再做修改(深拷贝)——再setState
  3. 不可以在setstate里使用push pop splice等

对象

  1. object.assign
  2. {...object, label:value}

可能是异步更新

// 异步

this.setState({

    count: this.state.count + 1

})

console.log(this.state.count) // 0



// 类似于$nextTick

this.setState({

    count: this.state.count + 1

},() => {

    console.log(this.state.count) // 1

})
// 同步

setTimeout(() => {

    this.setState({

        count: this.state.count + 1

    })

    console.log(this.state.count) // 1

}, 0)



// 自己定义的DOM事件——同步

hanshu(){

    document.body.addEventListener('click', () => {

        this.setState({

            count: this.state.count + 1        

        })

        console.log(this.state.count) // 1

    })

}

可能会被合并

异步更新——传入对象统一更新,传入函数会执行

this.setState({

   count: this.state.count + 1        

})

this.setState({

   count: this.state.count + 1        

})

this.setState({

   count: this.state.count + 1        

})

// 执行三次,结果为1 类似于

this.setState({

   count: 1        

})

this.setState({

   count: 1        

})

this.setState({

   count: 1        

})

// 执行三遍

this.setState((a, b) => {

    return {

       count: this.state.count + 1      

    }

})

this.setState((a, b) => {

    return {

       count: this.state.count + 1      

    }

})

this.setState((a, b) => {

    return {

       count: this.state.count + 1      

    }

})

// 函数不可被合并,只能一个一个执行

concat:追加,返回一个新数组,原数组值不变 = [...array, value]

4、组件生命周期:

三、React高级特性

1、受控组件&非受控组件:

受控组件:

1、value绑定this.state.value的值(checked)

2、onChange事件实现this.state.value的值的双向绑定

非受控组件:

必须操纵DOM时使用——获取上传文件的文件名时、富文本编辑器(传入、开发)

1、defaultValue绑定this.state.value的值(defaultChecked)

2、没有绑定onChange,只能通过ref获取DOM元素中的值

3、ref:

vue:直接绑定字符串,通过字符串拿到所选元素

react:需要在state里生命创建一个ref绑定的值,通过它选中DOM

this.InputNamne = React.createRef() // 创建react的ref对象方法

2、Portals

应对css兼容性问题

  1. 组件默认会按照既定层次嵌套渲染

  2. 需要将组件渲染到最外层

    1. position为fixed的时候,需要放在最外层渲染,兼容更多浏览器

react组件结构不变,写法还是嵌套写法,只是渲染位置变了

// ReactDom.creatPortal(HTML元素,渲染位置)

// protals用法

return ReactDom.createProtal(

    <div className="modal">{this.props.children}</div>

    document.body // DOM节点

)



// css

.modal{

    position: fixed;

    color: #ffffff;

    ......

}

使用场景:

  1. 父组件设置了BFC

  1. 父组件z-index太小
  2. position为fixed

3、Context 上下文

设置公共信息——主题颜色、语言——上传到每个组件中——redux大题小做

context方法分为数据的产生、修改以及消费,数据在(最高级别)父组件中产生

  • 使用React.createContext()方法创建一个context,并且可以传入一个默认值。

  • 在父组件中,使用ThemeContext.provider标签包裹住的所有标签都赋予了访问context值得权利

  • 为ThemeContext.provider绑定一个value值,可以实现对默认值的改变,(数据的产生与修改)

  • 数据的消费:

    • 类组件的消费

      • ThemeButton.contextType = ThemeContext 创建组件后,声明组建的contentType为最外层创建的context对象。
      • 在render函数内,直接使用this.context来获取主题内容
    • 函数组件的消费

      • 由于函数组件内部无实例,故无法使用this,所以需要<ThemeContext.Consumer>标签包裹,同时value作为返回值,可直接获取到context对象中的主题内容。
// 创建context对象

const ThemeContext = React.createContext('light')

// 最外层

/**

    1、数据产生方

*/

<ThemeContext.provider value={this.state.theme}> //最外层使用

    // 第一层

    <Tollbar/>

</ThemeContext>



/**

    1、数据消费

*/

// 函数组件

function Toolbar(props) {

    return {

        <div>

            // 第二层

            <ThemeButton />

            <ThemeLink />

        </div>    

    }

}



// class组件

/**

    1、数据使用

    2、向上查找,直到找到provider

*/

 class ThemeButton extends React.Component {

     render() {

         const theme = this.context

         return <div>

             <p>button's theme is {theme}</p>

         </div>     

     } 



 }

 ThemeButton.contextType = ThemeContext

 

 

 // 底层——函数组件

 function ThemeLink (props) {

     // const theme = this.context // 不可使用、函数组件无实例——没有this

     return <ThemeContext.Consumer>

         {value => <p>link`s theme is {value}</p>} // value就是ThemeContext.Consumer去找

     </ThemeContext.Consumer>

 }

4、异步组件

大组件进行异步加载,提高性能

在vue中使用:

  • import()方法实现

在react中使用:

  • React.lazy
  • React.Suspense
import React from 'react'



const ContextDemo = React.lazy(() => import('./ContextDemo'))



class App extends React.Component {

    constructor(props) {

        super(props)

    }

    render() {

        return <div>

            <p>引入一个动态组件</p>

            <hr />

            <React.Suspense fallback={<div>Loading...</div>}>

                <ContextDemo/>

            </React.Suspense>

        </div>



        // 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速)

        // 2. 看 network 的 js 加载

    }

}



export default App

5、性能优化

  • shouldComponentUpdate(SCU)
// 用法 判断是否继续执行render生命周期  默认返回true

shouldComponentUpdate(nextProps, nextState) {

    if (nextState.count !== this.state.count) { // this.props.type

        return true // 可以渲染

    }

    return false// 可以渲染

}
  • React 中,父组件有更新,子组件即使没有改变,也会渲染更新。
  • 默认SCU会返回true,使得无变化的子组件也触发更新,使得性能下降—— 需要才使用(配合不可变值)

react中直接push pop 。。。修改state中数组值得时候,不规范,违反不可变值,导致生命周期前后值相同,如果写了SCU校验会不通过导致bug

  • PureComponent(纯组件) 和 React.memo(类似备忘录的意思)

    • PureComponent:SCU实现浅比较,只取第一层 —— 配合不可变值
    • memo:函数组件中的 PureComponent
  • 不可变值immutable.js

6、组件公共逻辑的抽离

  • 高阶组件 HOC:

    • 并不是一种功能,而是一种模式
const HOCFactory = (Component) => {

    class HOC extends React.Component {

    // 再次定义多个组件的公共逻辑

        render(){

            return <Component {...this.props} /> // 返回拼装结果

        }

    }

    return HOC

}



const EnhancedComponent1 = HOCFactory(WrappedComponent1)

const EnhancedComponent2 = HOCFactory(WrappedComponent2)

Redux connect 是高阶组件

// connect 部分源码

export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {

    class Connect extends Component {

        constructor() {

            super()

            this.state = {

                allProps: {}

            }

        }

        // 省略代码......

        render () {

            return <WrappedComponent {...this.state.allProps} />

        }

    }

    return Connect

}
  • Render Props

7、Redux = Reducer + Flux(以前的设计,允许多个store)

  • Redux Flow —— redux工作流程

创建store

index.js

import { createStore } from 'redux'

import reducer from './reducer';



const store = createStore(reducer);



export default store

reducer.js

const defaultState = {

    inputValue: '234',

    list: [1, 3]

}



export default (state = defaultState, action) => {

    return state

}

使用

import store from './store/index'

import React, {Component} from 'react';



class TodoList extends Component {

  constructor(props){

    super(props);

    this.state = store.getState()

    console.log(this.state)

  }





  render() {

    return(

      <>

        <div>{this.state.inputValue}</div>

      </>

    )

  }

}



export default TodoList;

改变store里面的数据

使用

import store from './store/index'

import React, {Component} from 'react';



class TodoList extends Component {

  constructor(props){

    super(props);

    this.state = store.getState()

    this.handlestoreChange  = this.handlestoreChange.bind(this)

    this.handleChange  = this.handleChange.bind(this)

    store.subscribe(this.handlestoreChange)

  }





  render() {

    return(

      <>

        <input value={this.state.inputValue} onChange={this.handleChange}/>

        <button onClick={this.handleClick.bind(this)}>{"提交"}</button>

        {this.state.list.map((item, index) => {

          return(

            <div key={index}>{item}</div>

          )

        })}

      </>

    )

  }

  handleChange(e) {

    /* 创建action */

    const action = {

      type: 'change_input_value',

      value: e.target.value

    }

    /* 下发action */

    store.dispatch(action)

  }

  /* handleChange = (e) => {

     这种写法不能改变store的值

    console.log(e.target.value)

    this.setState({

      inputValue: e.target.value

    })

    console.log(store.getState()) 

  } */

  handleClick = () => {

    const action = {

      type: 'add_todo_action'

    }

    store.dispatch(action)

  }

  handlestoreChange() {

    /* 一旦感知到store变化,就使用getState重新拿store数据 */

    /* 再使用setState方法,更新页面绑定值 */

    this.setState(store.getState())

    console.log('store change')

  }

}



export default TodoList;

reducer.js

/* eslint-disable import/no-anonymous-default-export */

const defaultState = {

    inputValue: '234',

    list: [1, 3]

}



export default (state = defaultState, action) => {

    /* reducer 手册 记载着要干啥 */

    if (action.type === 'change_input_value') {

        const newState = JSON.parse(JSON.stringify(state))

        /* reducer可以接收state,不能改 */

        newState.inputValue = action.value

        /* 自动返回给store,store自动替换新数据 */

        return newState

    }

    if(action.type === 'add_todo_action'){

        const newState = JSON.parse(JSON.stringify(state))

        newState.list.push(newState.inputValue)

        newState.inputValue = ''

        return newState

    }

    return state

}

总结:

1、store是唯一得

2、只有store能改变自己的内容

3、reducer必须是纯函数(给固定输入就一定有固定输出,且无任何副作用)不能写ajax,日期函数等。修改了store值就叫做副作用。

8、redux中间件(补充)

单向数据流

可以对dispatch函数进行一些改造:

// 使用

import { applyMiddleware, createStore } from 'redux'

import creatLogger from 'redux-logger'

import thunk from 'redux-thunk'



const logger = createLogger();



const store = createStore(

    reducer,

    applyMiddleware(thunk, logger) // 可以扩展多个中间件,逗号隔开,会按顺序进行

)

自己修改dispatch

let next = store.dispatch



store.dispatch = function dispatchAndLog(action) {

    // 加一些逻辑

    next(action) // 执行原dispatch

    // 一些逻辑

}

9、React-router使用

1、哈希路由:使用:# 2C端使用

2、H5 history路由:无# 2B端可以使用

路由配置

function RouterComponent() {

    return(

        <Router>

            <Switch>

                <Route exact path="/">

                    <Home />

                </Router>

                <Route path="/project/:id">

                    <Project />

                </Router>

                <Route path="*">

                    <NotFound />

                </Router>

            </Switch>

        </Router>

    )

}
import React from 'react'

// Link用于跳转  useParams用来获取动态参数

import { Link, useParams } from 'react-router-dom'



function Project() {

    // 获取url参数 如'/Project/100'

    const { id } = useParams()

    

    return(

        <div>

            <Link to="/">首页</Link>

        </div>

    )

}



// 函数方法跳转路由

import { useHistory } from 'react-router-dom'



function Trash() {

    let history = useHistory()

    function handleClick() {

        history.push('/')

    }

    return(

        <div>

            <Button type="primary" onClick={handleClick}>回到首页</Button>

        </div>

    )

}

懒加载:lazy包裹 suspense包裹

在掘金看到一个大佬写的hook总结也好,建议大家阅读! 地址:juejin.cn/post/711893…