react使用

React JSX

XML 被设计用来传输和存储数据
HTML 被设计用来显示数据

  • JSX 就是 JavaScript,会发现react的写法与传统的HTML不一样,比如class 在react写成className,for在react写成htmlFor ,这些标签语法就是JSX。
  const blackBtn = 

JSX如何判断条件和渲染列表

react条件判断就和javasctipt一样:用if else,三元表达式,逻辑运算符 && ||

class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'
        }
    }
    render() {
        const blackBtn = 
        const whiteBtn = 

        // // if else
        if (this.state.theme === 'black') {
            return blackBtn
        } else {
            return whiteBtn
        }
        // // 三元运算符
        return 
{ this.state.theme === 'black' ? blackBtn : whiteBtn }
// && return
{ this.state.theme === 'black' && blackBtn }
} } export default ConditionDemo
  • React渲染
    要将React元素渲染到根DOM节点中,把React元素传递给 ReactDOM.render() 的方法来将其渲染到页面上

  • 渲染列表
    用map、key,react在render函数里面,把map遍历的对象,或者key遍历的对象放在{}表达式里面,把它渲染出来

React事件为何使用bind绑定this

Javascript是一种基于对象(object-based)的语言
class 的方式是 function 方式的语法糖,ES6中 class 的类型还是 function

  • JSX 回调函数中的 this,类的方法默认是不会绑定 this 的

  • react组件 ,this指向是指向当前的组件,当组件实例化之后,this指向的是新生成的实例,在新生成的实例使用this.state.xxx是找不到的,所以需要在构造器constructor重新绑定一下this

React 事件和 DOM 事件

event.preventDefault() 方法阻止元素发生默认的行为(如,当点击提交按钮时阻止对表单的提交)。

  • JSX 的语法进行事件处理,需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
    HTML 通常写法

React 中写法


React 中的event是一个合成事件,不是原生的Event

  • 通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面
  • index {index}; title {item.title}
  • // 传递参数 clickHandler4(id, title, event) { console.log(id, title) console.log('event', event) // 最后追加一个参数,即可接收 event }

    React表单

    在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新

    • react受控组件:类似双向数据绑定,不过在react中需要开发人员之间编写代码。比如用户在输入框input输入文字会同时更新显示到界面上
      设置了输入框 input 值 value = {this.state.value}。在输入框值发生变化时我们可以更新 value。我们可以使用 onChange 事件来监听 input 的变化,并修改 value
    class HelloMessage extends React.Component {
      constructor(props) {
          super(props);
          this.state = {value: 'Hello word!'};
          this.handleChange = this.handleChange.bind(this);
      }
     
      handleChange(event) {
        this.setState({value: event.target.value});
      }
      render() {
        var value = this.state.value;
        return 

    {value}

    ; } } ReactDOM.render( , document.getElementById('example') );
    • 非受控组件
      非受控也就意味着我可以不需要设置它的state属性,而通过ref来操作真实的DOM
    父组件
    调用:let dataref=this.onRef.getData();
      console.log('dataref',dataref)
    //调用子组件
        (this.onRef)=r} />
       
    //子组件
      getData=()=>{
          console.log('获取数据')
        }
    

    React父子组件通讯

    通讯是单向的,数据必须是由一方传到另一方

    1.父组件与子组件间的通信
    在 React 中,父组件可以向子组件通过传 props 的方式,向子组件进行通讯

    2.子组件传值父组件
    子组件通过调用父组件传递到子组件的方法向父组件传递消息的

    //子组件
    this.props.listCall(list);
    listCall=()=>{
    }
    <子组件  listCall={listCall}/>
    
    1. 父组件管理数据
    2. 子组件渲染数据
    3. 表单提交数据
      react处理数据,把状态或者数据提到最高的组件上,最高级别的组件管理数据,然后向list子组件下发数据,list子组件渲染数据,input输入框下发一个函数,input组件执行这个事件,最高级别的组件负责把数据拼接好,执行完之后再在知list子组件
    import React from 'react'
    import PropTypes from 'prop-types'
    
    class Input extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                title: ''
            }
        }
        render() {
            return 
    } onTitleChange = (e) => { this.setState({ title: e.target.value }) } onSubmit = () => { 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
      {list.map((item, index) => { return
    • {item.title}
    • })}
    } } // props 类型检查 List.propTypes = { list: PropTypes.arrayOf(PropTypes.object).isRequired } class Footer extends React.Component { constructor(props) { super(props) } render() { return

    {this.props.text} {this.props.length}

    } 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
    } onSubmitTitle = (title) => { this.setState({ list: this.state.list.concat({ id: `id-${Date.now()}`, title }) }) } } export default TodoListDemo

    setState为何使用不可变值

    • 在修改状态时千万不能改变原来的状态state,修改后的数据不能影响原来的数据,是因为在react中的shouldMountUpdate声明周期数据将要改变的值与之前的数据做个比较,来决定是否更新,以setState不可变值来作为性能优化。

    ssetState是同步还是异步

    • setState函数中传入的是对象,是异步的,拿不到最新的值
    • setState函数传入的是回调函数,是同步的,可以拿到最新的值
     this.setState({
         count: this.state.count + 1
       }, () => {
         console.log('count by callback', this.state.count) // 回调函数中可以拿到最新的 state
      })
     console.log('count', this.state.count) // 异步的,拿不到最新值
    
    • setTimeout函数里面的ssetState是同步的,可以拿到最新的值
      // setTimeout 中 setState 是同步的
        setTimeout(() => {
         this.setState({
               count: this.state.count + 1
           })
            console.log('count in setTimeout', this.state.count)
        }, 0)
    
    • 自己定义的 DOM 事件,setState 是同步的
     bodyClickHandler = () => {
        this.setState({
           count: this.state.count + 1
         })
        console.log('count in body event', this.state.count)
    }
    componentDidMount() {
      // 自己定义的 DOM 事件,setState 是同步的
        document.body.addEventListener('click', this.bodyClickHandler)
    }
    
    • state 异步更新的话,更新前会被合并,异步更新的话是传入对象,会被合并(类似 Object.assign ),执行结果只一次 +1
        this.setState({
                count: this.state.count + 1
            })
            this.setState({
                count: this.state.count + 1
            })
            this.setState({
                count: this.state.count + 1
            })
    

    -state同步更新的话,不会被合并, 传入函数,执行结果是 +3

     this.setState((prevState, props) => {
                return {
                    count: prevState.count + 1
                }
            })
            this.setState((prevState, props) => {
                return {
                    count: prevState.count + 1
                }
            })
            this.setState((prevState, props) => {
                return {
                    count: prevState.count + 1
                }
            })
    

    React组件生命周期

    https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

    react组件不是一个真正的Dom,react会生成一个虚拟Dom,虚拟Dom会经历挂载、更新,销毁阶段。

    • 挂载:组件挂载到dom树上
    • 更新:重新渲染页面
    • 删除:组件从dom树上删除
    1. 挂载过程
      constructor:调用构造函数
      componentWillMount:组件将要更新
      render:组件渲染到页面上
      componentDidMount:组件已经加载或者已经挂载到页面上。

    2. 更新过程
      componentWillReceiveProps:组件将要接受参数
      shouldComponentUpdata:组件是否应该更新,必须retruntrue或者false,true表示更新,false表示不更新。
      componentWillUpdata:组件将要更新
      render:组件渲染到页面
      getSnaphotBeforeUpdata:获取截图在更新前
      componentWillUpdata:组件已经更新

    3. 卸载过程
      componentWillUnmount:组件讲要卸载

    React基本使用总结

    • JSX基本使用
    • 条件渲染条件判断
    • 列表渲染
    • setState
    • 生命周期
    • 事件
    • 表单
    • 组件和props(组件之间的通讯)

    React函数组件和calss组件的区别

    • 纯函数,输入props,输出JSX
    • 没有实例,没有生命周期,没有state
    • 不能扩展其他方法

    什么是react非受控组件

    1. 非受控组件
    • ref
    • defaultValue defaultChecked
    • 手动操作DOM元素

    非受控组件使用场景

    • 必须手动操作DOM 元素,setSate实现不了
    • 文件上传
    • 某些富文本编辑器,需要传入DOM元素

    注意:优先使用受控组件,符合React设计原则,必须操作DOM时,在使用非受控组件(操作DOM影响性能)

    import React from 'react'
    
    class App extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                name: 'xiaowu',
                flag: true,
            }
            this.nameInputRef = React.createRef() // 创建 ref
            this.fileInputRef = React.createRef()
        }
        render() {
            // input defaultValue
            return 
    {/* 使用 defaultValue 而不是 value ,使用 ref */} {/* state 并不会随着改变 */} state.name: {this.state.name}
    // checkbox defaultChecked return
    // file return
    } alertName = () => { const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点 alert(elem.value) // 不是 this.state.name } alertFile = () => { const elem = this.fileInputRef.current // 通过 ref 获取 DOM 节点 alert(elem.files[0].name) } } export default App

    什么场景需要用React Portals

    • 组件渲染到父组件以外
      组件默认会按照既定层次嵌套渲染
      // 正常渲染
             return 
    {this.props.children} {/* vue slot */}
    // 使用 Portals 渲染到 body 上。 // fixed 元素要放在 body 上,有更好的浏览器兼容性。 return ReactDOM.createPortal(
    {this.props.children}
    , document.body // DOM 节点 )

    React Context

    Context使用场景

    • 公共信息(语言、主题)如何传递给每个子组件
    • 用props太繁琐
    • 用redux没有必须要
    import React from 'react'
    
    // 创建 Context 填入默认值(任何一个 js 变量)
    const ThemeContext = React.createContext('light')
    
    // 底层组件 - 函数是组件
    function ThemeLink (props) {
        // const theme = this.context // 会报错。函数式组件没有实例,即没有 this
    
        // 函数式组件可以使用 Consumer
        return 
            { value => 

    link's theme is {value}

    }
    } // 底层组件 - class 组件 class ThemedButton extends React.Component { // 指定 contextType 读取当前的 theme context。 // static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext render() { const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。 return

    button's theme is {theme}

    } } ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。 // 中间的组件再也不必指明往下传递 theme 了。 function Toolbar(props) { return (
    ) } class App extends React.Component { constructor(props) { super(props) this.state = { theme: 'light' } } render() { return
    } changeTheme = () => { this.setState({ theme: this.state.theme === 'light' ? 'dark' : 'light' }) } } export default App

    React异步加载组件

    • import()
    • 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 

    引入一个动态组件


    Loading...
    }>
    // 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速) // 2. 看 network 的 js 加载 } } export default App

    性能优化

    react性能优化, 其主要目的就是防止不必要的子组件渲染更新。

    • setSate不可变值
    • shouldComponentUpdate
    • PureComponet和React.memo
    • 不可变值immutable.js
      使用shouldComponentUpdat生命周期函数,如果更新之后的值不等于this.state.count的值可以渲染,否则不能渲染,减少渲染操作次数,优化性能

    使用PureComponentPureComponent 对状态的对比是浅比较,当父组件修改跟子组件无关的状态时,再也不会触发自组件的更新了

    // 子组件
    import React, { PureComponent } from 'react';
    class SonComponent extends PureComponent {
      render() {
        const { sonMsg } = this.props;
        console.log('sonMsg', sonMsg + ' || ' + Date.now());
        return (
        
    {sonMsg}
    ) } } export default SonComponent;
    // 父组件
    import React, { Component } from 'react';
    import './App.css';
    import SonComponent from './SonComponent';
    
    class FatherComponent extends Component {
      constructor() {
        super();
        this.state = {
          fatherMsg: "who's your daddy",
          sonMsg: {
            val: "I'm son"
          }
        }
      }
    
      render() {
        const { fatherMsg, sonMsg } = this.state;
        console.log('fatherMsg', fatherMsg);
        return (
          
    ); } } export default FatherComponent;

    React.memo 为高阶组件,它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。

    function Mycomponet(props){
        // 使用props渲染
    }
    function areEqual(prevProps,nextProps){
        //如果把nextProps(更新之后的数据)传入render方法的返回结果与将prevProps(更新之前的数据)传入render方法的返回结果一致返回ture,否则返回false
    }
    export default React.memo(Mycomponet,areEqual)
    

    参考:https://www.jianshu.com/p/3275e2b9a928

      // 演示 shouldComponentUpdate 的基本使用
        shouldComponentUpdate(nextProps, nextState) {
            if (nextState.count !== this.state.count) {
                return true // 可以渲染
            }
            return false // 不重复渲染
        }
    

    React性能优化shouldComponentUpdate默认返回什么

    shouldComponentUpdate默认返回true

    React性能优化-shouldComponentUpdate配合不可变值

    • shouldComponentUpdate默认返回true
    • 必须配合不可变值一起使用
    • 可先不用shouldComponentUpdate,有性能问题时再考虑使用
     // 增加 shouldComponentUpdate
        shouldComponentUpdate(nextProps, nextState) {
            // _.isEqual 做对象或者数组的深度比较(一次性递归到底)
            if (_.isEqual(nextProps.list, this.props.list)) {
                // 相等,则不重复渲染
                return false
            }
            return true // 不相等,则渲染
        }
    //配合不可变值使用
     onSubmitTitle = (title) => {
            this.setState({
                list: this.state.list.concat({   //使用concat,原数据不影响新数据,故不可变值
                    id: `id-${Date.now()}`,
                    title
                })
            })
        }
    

    完整代码

    class Input extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                title: ''
            }
        }
        render() {
            return 
    } onTitleChange = (e) => { this.setState({ title: e.target.value }) } onSubmit = () => { const { submitTitle } = this.props submitTitle(this.state.title) 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
      {list.map((item, index) => { return
    • {item.title}
    • })}
    } // 增加 shouldComponentUpdate shouldComponentUpdate(nextProps, nextState) { // _.isEqual 做对象或者数组的深度比较(一次性递归到底) if (_.isEqual(nextProps.list, this.props.list)) { // 相等,则不重复渲染 return false } return true // 不相等,则渲染 } } // props 类型检查 List.propTypes = { list: PropTypes.arrayOf(PropTypes.object).isRequired } 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' } ] } } render() { return
    } onSubmitTitle = (title) => { // 正确的用法 this.setState({ list: this.state.list.concat({ id: `id-${Date.now()}`, title }) }) // // 为了演示 SCU ,故意写的错误用法 // this.state.list.push({ // id: `id-${Date.now()}`, // title // }) // this.setState({ // list: this.state.list // }) } } export default TodoListDemo

    React性能优化-PureComponent和memo

    • PureComponent,shouldComponentUpdate中实现了浅比较
    • memo,函数组件中的PureComponent
    • 浅比较已使用大部分情况(尽量不要做深度比较)
    class Input extends React.Component {}
    class List extends React.PureComponent {
    ...
     shouldComponentUpdate() {/*浅比较*/}
    }
    
    

    完整代码

    import React from 'react'
    import PropTypes from 'prop-types'
    
    class Input extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                title: ''
            }
        }
        render() {
            return 
    } onTitleChange = (e) => { this.setState({ title: e.target.value }) } onSubmit = () => { const { submitTitle } = this.props submitTitle(this.state.title) this.setState({ title: '' }) } } // props 类型检查 Input.propTypes = { submitTitle: PropTypes.func.isRequired } class List extends React.PureComponent { constructor(props) { super(props) } render() { const { list } = this.props return
      {list.map((item, index) => { return
    • {item.title}
    • })}
    } shouldComponentUpdate() {/*浅比较*/} } // props 类型检查 List.propTypes = { list: PropTypes.arrayOf(PropTypes.object).isRequired } 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' } ] } } render() { return
    } onSubmitTitle = (title) => { // 正确的用法 this.setState({ list: this.state.list.concat({ id: `id-${Date.now()}`, title }) }) // // 为了演示 SCU ,故意写的错误用法 // this.state.list.push({ // id: `id-${Date.now()}`, // title // }) // this.setState({ // list: this.state.list // }) } } export default TodoListDemo

    React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 props 和 state 较为简单时,才使用 React.PureComponent,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。你也可以考虑使用 immutable 对象加速嵌套数据的比较

    React.memo()是一个高阶函数,它与 React.PureComponent类似,但是一个函数组件而非一个类

    function Child({seconds}){
        console.log('I am rendering');
        return (
            
    I am update every {seconds} seconds
    ) }; export default React.memo(Child)

    React性能优化-了解immutable.js

    • 彻底拥抱"不可变值"
    • 基于共享数据(不是深拷贝),速度好
    • 有一定的学习和迁移成本,按需使用

    影响性能的关键,Javascript中的对象一般都是可变的,因为多数都是引用赋值

    React高阶组件

    • 关于公共组件逻辑的抽离
      高阶组件HOC
      Render Props

    高阶组件只是一个包装了另外一个 React 组件的 React 组件

    import React from 'react'
    
    // 高阶组件
    const withMouse = (Component) => {
        class withMouseComponent extends React.Component {
            constructor(props) {
                super(props)
                this.state = { x: 0, y: 0 }
            }
      
            handleMouseMove = (event) => {
                this.setState({
                    x: event.clientX,
                    y: event.clientY
                })
            }
      
            render() {
                return (
                    
    {/* 1. 透传所有 props 2. 增加 mouse 属性 */}
    ) } } return withMouseComponent } const App = (props) => { const a = props.a const { x, y } = props.mouse // 接收 mouse 属性 return (

    The mouse position is ({x}, {y})

    {a}

    ) } export default withMouse(App) // 返回高阶函数

    什么是React Render Props

    • 高级函数,模式简单,但会增加组件层级
    • Render Props代码简洁,学习成本高
    • 按需使用
    import React from 'react'
    import PropTypes from 'prop-types'
    
    class Mouse extends React.Component {
        constructor(props) {
            super(props)
            this.state = { x: 0, y: 0 }
        }
      
        handleMouseMove = (event) => {
          this.setState({
            x: event.clientX,
            y: event.clientY
          })
        }
      
        render() {
          return (
            
    {/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */} {this.props.render(this.state)}
    ) } } Mouse.propTypes = { render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数 } const App = (props) => (

    {props.a}

    The mouse position is ({x}, {y})

    }/>
    ) /** * 即,定义了 Mouse 组件,只有获取 x y 的能力。 * 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。 */ export default App

    高级特性

    • 性能优化
    • 高阶组件HOC
    • Render Props

    Redux使用

    • 和Vuex作用相同,但比Vuex学习成本高
    • 不可变值,纯函数
    1. 基本概念
      全局数据状态管理工具(状态管理机),用来做组件通信等。
      Redux 的适用场景:多交互、多数据源
      某个组件的状态,需要共享
      某个状态需要在任何地方都可以拿到
      一个组件需要改变全局状态
      一个组件需要改变另一个组件的状态

    2. 单项数据流

    3. react-redux
      4.异步action
      5.中间件

    UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性

    用户的使用方式非常简单
    用户之间没有协作
    不需要与服务器大量交互,也没有使用 WebSocket
    视图层(View)只从单一来源获取数据

    Redux单项数据流

    基本概念

    • store state

    Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store
    如果想拿到某个数据,Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。当前时刻的 State,可以通过store.getState()拿到

    • action

    State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了

    • reducer

    Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
    Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State;

    单项数据流的概述

    • dispatch(action)

    store.dispatch()是 View 发出 Action 的唯一方法

    • reducer-newState

    State 的计算过程就叫做 Reducer

    • subscribe触发通知

    Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数

    react-redux

    • connect

    React-Redux 提供Provider组件,可以让容器组件拿到state

    • connect

    React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来

    • mapStateToProps mapDispatchToProps

    react-redux中构造出了state与dispatch,connect中的两个参数就可以将它们引入props

    Redux action如何处理异步

    • redux-thunk
    • redux-promise
    • redux-sage

    简述Redux中间件原理

    2021-03-01_163154.jpg

    React-routers

    React-router使用

    • 路由模式(hash、H5 histoty)同vue-router
    • 路由配置(动态路由、懒加载)同vue-router

    React-router路由模式
    hash模式(默认),比如:http://abc.com/#/user/10
    H5 histoty模式,比如: http://abc.com/user/10
    后者需要server端支持,因此无特殊需求可选择前者

    你可能感兴趣的:(react使用)