react使用

React JSX

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

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

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

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

class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'
        }
    }
    render() {
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>

        // // if else
        if (this.state.theme === 'black') {
            return blackBtn
        } else {
            return whiteBtn
        }
        // // 三元运算符
        return <div>
            { this.state.theme === 'black' ? blackBtn : whiteBtn }
        </div>

        // &&
        return <div>
            { this.state.theme === 'black' && blackBtn }
        </div>
    }
}
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 通常写法
<button onclick="activateLasers()">
  激活按钮
</button>

React 中写法

<button onClick={activateLasers}>
  激活按钮
</button>

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

  • 通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面
<li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>
        index {index}; title {item.title}
</li>
// 传递参数
    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 <div>
            <input type="text" value={value} onChange={this.handleChange} /> 
            <h4>{value}</h4>
           </div>;
  }
}
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);
  • 非受控组件
    非受控也就意味着我可以不需要设置它的state属性,而通过ref来操作真实的DOM
父组件
调用:let dataref=this.onRef.getData();
  console.log('dataref',dataref)
//调用子组件
    <RefTest  ref={r=>(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 <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    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 <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

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实现不了
  • 文件上传<input type=file>
  • 某些富文本编辑器,需要传入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 <div>
            {/* 使用 defaultValue 而不是 value ,使用 ref */}
            <input defaultValue={this.state.name} ref={this.nameInputRef}/>
            {/* state 并不会随着改变 */}
            <span>state.name: {this.state.name}</span>
            <br/>
            <button onClick={this.alertName}>alert name</button>
        </div>

        // checkbox defaultChecked
        return <div>
            <input
                type="checkbox"
                defaultChecked={this.state.flag}
            />
        </div>

        // file
        return <div>
            <input type="file" ref={this.fileInputRef}/>
            <button onClick={this.alertFile}>alert file</button>
        </div>

    }
    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 <div className="modal">
         {this.props.children} {/* vue slot */}
       </div> 

        // 使用 Portals 渲染到 body 上。
        // fixed 元素要放在 body 上,有更好的浏览器兼容性。
        return ReactDOM.createPortal(
            <div className="modal">{this.props.children}</div>,
            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 <ThemeContext.Consumer>
        { value => <p>link's theme is {value}</p> }
    </ThemeContext.Consumer>
}

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

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'light'
        }
    }
    render() {
        return <ThemeContext.Provider value={this.state.theme}>
            <Toolbar />
            <hr/>
            <button onClick={this.changeTheme}>change theme</button>
        </ThemeContext.Provider>
    }
    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 <div>
            <p>引入一个动态组件</p>
            <hr />
            <React.Suspense fallback={<div>Loading...</div>}>
                <ContextDemo/>
            </React.Suspense>
        </div>

        // 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 (
    <div>{sonMsg}</div>
    )
  }
}

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 (
      <div>
        <button
          onClick={() => {
            sonMsg.val = 'son' + Date.now();
            this.setState({ sonMsg })}
          }>
          变换sonMsg值</button>
        <SonComponent sonMsg={sonMsg}></SonComponent>
      </div>
    );

  }
}

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 <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    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 <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }

    // 增加 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 <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
        </div>
    }
    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 <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    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 <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }
    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 <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
        </div>
    }
    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 (
        <div>I am update every {seconds} seconds</div>
    )
};
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 (
                <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
                    {/* 1. 透传所有 props 2. 增加 mouse 属性 */}
                    <Component {...this.props} mouse={this.state}/>
                </div>
            )
        }
    }
    return withMouseComponent
}

const App = (props) => {
    const a = props.a
    const { x, y } = props.mouse // 接收 mouse 属性
    return (
        <div style={{ height: '500px' }}>
            <h1>The mouse position is ({x}, {y})</h1>
            <p>{a}</p>
        </div>
    )
}
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 (
        <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
            {/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */}
            {this.props.render(this.state)}
        </div>
      )
    }
}
Mouse.propTypes = {
    render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数
}

const App = (props) => (
    <div style={{ height: '500px' }}>
        <p>{props.a}</p>
        <Mouse render={
            /* render 是一个函数组件 */
            ({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>
        }/> 
    </div>
)
/**
 * 即,定义了 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

  • <Provider>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端支持,因此无特殊需求可选择前者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271