• 0react


    vscode

    光标左击选中某个变量,然后CTRL+Shift+L 选中所有的目标变量 改名字

    react开发依赖

     <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.development.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.development.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/6.26.0/babel.js"></script>
    
     <script type="text/babel"></script>
    

    初体验:切换文本

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.development.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.development.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/6.26.0/babel.js"></script>
    
    
    </head>
    
    <body>
      <div id="app"></div>
    
    
      <script type="text/babel">
        let flag = true
        function render() {
          const context = <div><h2>hello {flag ? 'world' : 'react'}</h2> <button onClick={changeText}>切换文字</button></div>
          ReactDOM.render(context, document.getElementById('app'))
    
        }
        render()
    
        function changeText() {
          flag = !flag //改变数据,需要手动重新 调用ReactDOM.render
          render()
    
        }
    
      </script>
    </body>
    
    </html>
    

    class改进

    1. 响应数据要放在state里面。
    2. 更新state里面数据一定要调用this.setState()方法。这样react才会自动调用render函数。
    3. 所以永远不要手动调用render()函数。
    
    <body>
      <div id="app"></div>
    
    
      <script type="text/babel">
        class App extends React.Component {
          constructor() {
            super()
            // this.state控制响应式数据
            this.state = {
              flag: true,
    
    
            }
          }
          // 永远不用手动调用render()函数,没有任何效果。
          render() {
            return (
              <div>
                <h2>hello {this.state.flag ? 'world' : 'react'}</h2>
                <button onClick={this.changeText}>切换文本</button>
              </div>
            )
          }
          changeText = () => {
            // this.state.flag = !this.state.flag //state里面的flag确实改变,但是页面不会自动渲染更新,render函数不会执行
            // 要想自动更新,需要调用setState方法。这样才会通知render函数去自动执行
            this.setState({
              flag: !this.state.flag //只会更新自己要改变的值。其他在state里的属性和值都不会丢失。保持原样
            })
    
    
          }
    
        }
    
        ReactDOM.render(<App></App>, document.getElementById('app'))
    
    
      </script>
    </body>
    

    jsx

    jsx语法书写规范:

    1. jsx顶层只能有一个根元素
    2. render函数return 后面带一个小括号().小括号里面写任何的jsx语法。不带小括号就不能换行了。
    3. jsx里面HTML元素可以写单标签和双标签。如果是单标签,必须是带/闭合
    4. jsx注释{/注释注释注释/}
     render() {
            return (
              <div>
                <h3>{this.state.msg}</h3>
                {/*
                  我是注释,可以一行
                  我是注释可以多行
                */}
                <img src="" alt="" />
              </div>
            )
          }
    

    jsx变量

    在{}中可以在页面正常显示的数据类型有:string|number|Array.
    在{}中不会在页面显示的数据类型有:null|undefined|boolean.但是不会报错。如果想显示,转为字符串类型。

    在{}中绝对不能放Object .

    {/* 放变量,放变量表达式,放三目运算符,放函数*/}
    <h3>hello world</h3>
    <h3>hello {this.state.msg}</h3>
    <h3>{this.state.demo1?'a':'b'}</h3>
    <h3>{this.showInfo(this.state.user)}</h3>
    
    

    绑定属性

    <script type='text/babel'>
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              title: "I am title",
              imgSrc: './imgs/logo192.png',
              reactUrl: 'https://react.docschina.org/docs/introducing-jsx.html',
              active: true,
              marginRight: 30
            }
          }
          render() {
            let { title, imgSrc, reactUrl, active, marginRight } = this.state
            return (
              <div>
                <h3 title={title}>绑定title</h3>
                <img src={imgSrc} width="30"></img>
                <a href={reactUrl}>react 官网</a>
                <h3 className="demo demo1 demo2">class样式是className 不是class避免es6 class关键字 </h3>
                <h3 className={"demo0 demo1 " + (active ? 'active' : '')}>动态切换是否添加active class样式</h3>
                <h3 className={['demo', 'demo', active ? 'active' : ''].join(' ')}>动态绑定class方式2</h3>
                <h3 className={`demo0 demo1 ${active ? 'active' : ''}`}>动态绑定class方式3</h3>
                <label htmlFor="aa">for在js中是关键字,所以label要避讳</label>
                <h3 style={{ color: 'red', fontSize: '30px', marginRight:marginRight }}>绑定内联样式数字自动带px</h3>
    
              </div>
            )
          }
        }
        ReactDOM.render(<App />, document.getElementById('app'))
      </script>
    

    虚拟DOM

    jsx语法本质是React.createElement()的语法糖,生成一个ReactElement对象树(js对象),这个对象数就是虚拟DOM对象,保存在内存中。然后通过ReactDOM.render()函数渲染成真实的DOM对象。

    <body>
      <div id="app"></div>
      <script type="text/babel">
        const message0 =
          <div className="wrap">
            <header className='header'>
              <h2>i am header</h2>
            </header>
            <main className="content">
              <h3>i am content</h3>
    
            </main>
    
          </div>
        const message =
          React.createElement("div", { className: "wrap" },
            React.createElement("header", { className: "header" }, React.createElement("h2", null, "i am header")),
            React.createElement("main", { className: "content" }, React.createElement("h3", null, "i am content")));
          console.log(message);
        ReactDOM.render(message, document.getElementById('app'))
      </script>
    
    </body>
    

    react事件处理其传参

    • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
    • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
    <script type='text/babel'>
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              message: 'hello react',
              num:300
            }
            // this.handleClick = this.handleClick.bind(this)
          }
          render() {
            return (
              <div>
                {/*优选使用事件传参1 */}
                <button onClick={(e) => { this.handleClick(e, 100) }}>事件传参方式1 this是render函数中的this</button>
                <button onClick={this.handleClick1.bind(this, 100)}>事件传参方式2</button>
                <button onClick={this.handleClick2(100)}>事件传参方式3</button>
              </div>
            )
    
          }
          handleClick(e, params) {
            console.log(e, params);
    
          }
          handleClick1(num, e) {
            console.log(num, e);
          }
          handleClick2 = (num) => {
            return e => {
              console.log(e);
              console.log(num);
            }
          }
        }
        ReactDOM.render(<App />, document.getElementById('app'))
      </script>
    

    条件渲染

    模拟实现v-if

    v-if作用1:显示不同内容

    render() {
            const { isLogin, flag, moiveList } = this.state
            const txt = isLogin ? '登录' : '退出'
            const isShowH3 = flag ? 'block' : 'none'
            const h3Style = { color: 'red', display: isShowH3 }
            return (
              <div>
                {/* v-if if控制显示内容方式1 判断逻辑放在外面*/}
                <h3>{txt}</h3>
                {/*v-if if控制显示内容方式2 三目表达式*/}
                <h3>{isLogin ? '登录' : '退出'}</h3>
    
    
              </div>
            )
    
          }
    

    v-if作用2:控制是否渲染&&

    render() {
            const { isLogin, flag, moiveList } = this.state
            const txt = isLogin ? '登录' : '退出'
            const isShowH3 = flag ? 'block' : 'none'
            const h3Style = { color: 'red', display: isShowH3 }
            return (
              <div>
       
                {/* 这个h3标签不管是true/false都会渲染出*/}
                <h3>{isLogin && '登录'}</h3> 
                
                {/* 这个最好; 只有当isLogin是true的时候还会渲染h3标签*/}
                {isLogin && <h3>登录</h3>} 
                 
                 {/* 只有当moiveList列表长度>0才遍历渲染数组 */}
                {moiveList.length > 0 &&
                  <ul>
                    {moiveList.map((item, index) => <li key={index}>{item}</li>)}
                  </ul>
                }
    
              </div>
            )
    
          }
    

    模拟实现v-show css的display

    render() {
            const { isLogin, flag, moiveList } = this.state
            const isShowH3 = flag ? 'block' : 'none'
            const h3Style = { color: 'red', display: isShowH3 }
            return (
              <div>
    
                {/*模拟实现v-show  css的display属性*/}
                <h3 style={h3Style}>hello react</h3>
    
    
              </div>
            )
    
          }
    

    列表和对象遍历&key

    首先要明确对象不能在react遍历。需要变样遍历。

    <script type='text/babel'>
    
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              message: 'hello world',
              movieList: ['少年派', '变形金刚', '三国演义', '大话西游'],
              user: { name: 'zs', age: 10 }
            }
          }
          render() {
            return (
              <div>
                <h3>{this.state.message}</h3>
                <h2>电影列表</h2>
                <ul>//遍历列表
                  {
                    this.state.movieList.map((item, index) => {
                      return <li key={index}>{item}</li>
                    })
    
                  }
                </ul>
                <h3>user信息</h3>
                <div>//遍历对象
                  {
                    Object.keys(this.state.user).map(key => {
                      return <p key={key}>{this.state.user[key]}</p>
                    })
                  }
                </div>
              </div>
            )
          }
    
        }
        ReactDOM.render(<App />, document.getElementById('app'))
    
      </script>
    

    bookstore综合练习

    dk5Bc9.png

    1. 表格显示内容
    2. 底部显示总价格
    3. 点击+ -增加或者减少书数量。但是不能减少为负数
    4. 点击删除按钮移除一行数据,当移除所有书,显示购物车为空。
    
    <body>
      <div id="app"></div>
      <script type="text/babel">
        class App extends React.Component {
          constructor() {
            super()
            this.state = {
              bookStoreList: [
                { title: '算法导论', publicTime: '2006-09', price: 86.00, count: 0, },
                { title: '编程艺术', publicTime: '2006-02', price: 59.00, count: 0, },
                { title: '高效java', publicTime: '2008-10', price: 39, count: 0, },
                { title: '代码大全', publicTime: '2006-03', price: 128.00, count: 0 },
              ],
    
    
            }
          }
          initTable() {
            const { bookStoreList } = this.state
            return (
              <div>
                <table className="table table-striped table-bordered table-sm">
                  <thead>
                    <tr>
                      <th scope="col">#</th>
                      <th scope="col">书本名称</th>
                      <th scope="col">出版日期</th>
                      <th scope="col">价格</th>
                      <th scope="col">购买数量</th>
                      <th scope="col">操作</th>
                    </tr>
                  </thead>
                  <tbody>
                    {bookStoreList.map((item, index) => {
                      return (
                        <tr key={item.title}>
                          <th scope="row">{index + 1}</th>
                          <td>{item.title}</td>
                          <td>{item.publicTime}</td>
                          <td>{this.formatPrice(item.price)}</td>
                          <td style={{  150 }}>
                            <button type="button" className="btn btn-success" onClick={() => this.handleAdd(item.title,)}>+</button>
                            <span className="count">{item.count}</span>
                            <button type="button"
                              className="btn btn-primary"
                              disabled={item.count <= 0}
                              onClick={() => this.decrease(item.title,)}>-</button>
                          </td>
                          <td><button
                            type="button"
                            className="btn btn-danger"
                            onClick={() => this.deleteItem(item.title)}
                          >移除</button> </td>
                        </tr>
                      )
                    })}
    
                  </tbody>
                </table>
                {/*计算总价格*/}
                {this.computeTotal()}
    
              </div>
            )
          }
          renderTips() {
            return <p>购物车为空</p>
          }
          render() {
            return this.state.bookStoreList.length > 0 ? this.initTable() : this.renderTips()
          }
          formatPrice(price) {
            return typeof price === 'number' ? `¥${price.toFixed(2)}` : price
          }
          //计算总价格 注意:其他函数(非render函数)里面也可以使用jsx语法。
          computeTotal() {
            const totalPrice = this.formatPrice(this.state.bookStoreList.reduce((prev, next) => prev + next.price * next.count, 0))
            return <p>总价格是: <span style={{ color: 'red', fontSize: 20, fontWeight: 700 }}>{totalPrice}</span></p>
    
          }
          //增加数量
          handleAdd(title) {
            {/*this.state.bookStoreList不要改动*/ }
            this.setState({ bookStoreList: this.state.bookStoreList.map(item => item.title === title ? { ...item, count: ++item.count } : item) })
    
          }
          //减少数量
          decrease(title) {
    
    
            const res = this.state.bookStoreList.map(item => item.title === title ? { ...item, count: --item.count } : item)
            this.setState({ bookStoreList: res })
          }
          // 移除 
          deleteItem(bookTitle) {
            //react中设计原则:state数据不可变性。
            this.setState({ bookStoreList: this.state.bookStoreList.filter(item => item.title !== bookTitle) })//filter函数不会修改this.state.bookStoreList的值
    
          }
        }
        ReactDOM.render(<App></App>, document.getElementById('app'))
    
      </script>
    
    </body>
    
    

    全局安装yarn

    node 包管理工具

    npm install yarn -g

    yarn --version

    yarn add package === npm i package -S 运行依赖

    yarn add package -D ===npm i pageage -D 开发依赖

    yarn remove package === npm uninstall package -S/D

    yarn cache clean ===npm chahe clean

    yarn upgrade

    全局安装react手脚架

    npm install create-react-app -g

    create-react-app --version

    创建react项目

    create-react-app projectName 创建项目

    cd projectName

    yarn start

    projectName不能使用大写字母,多个单词以-连接。eg: mail-store-h5

    react vscode插件和快捷键

    user.json加入。写标签自动闭合
    "emmet.includeLanguages": {
    "javascript": "javascriptreact"
    }
    
    imrc  -> import React, { Component } from 'react' 
    ccc  ->快速生成类组件
    rcc  ->imrc+ccc
    rconst ->constructor函数
    rpcp  -> proptypes
    rfc  ->函数式组件
    
    
    //rpcp
    import React, { PureComponent } from 'react'
    import PropTypes from 'prop-types'
    
    export default class FileName extends PureComponent {
      static propTypes = {}
    
      render() {
        return <div>$2</div>
      }
    }
    
    

    react最开始的初始化

    也就是删除一些文件而已

    dALngK.png

    react组件化开发

    组件思想:

    数据逻辑和ui组件的分离

    组件划分:

    根据组件定义划分:函数组件(没有内部状态|没有生命周期)和类组件

    根据组件内部是否有状态需要维护:无状态组件(stateless Component)和有状态组件(stateful Component)

    根据组件的职责:展示性组件和容器类组件

    异步组件、高阶组件等

    类组件

    1. 定义组件时组件名大写字母开头,必须继承React.Component ;类组件必须实现render函数。
      1. eg: class News extends React.Component{}
      2. render函数的返回值:react元素(html元素|自定义react组件),数组(普通数组|jsx数组),基本数据类型(number|string|boolean(页面看不见))
    2. constructor()函数可选;this.state()里面放组件数据
    3. 作为组件使用: 而使用是错的。

    react 类组件生命周期

    https://zh-hans.reactjs.org/docs/react-component.html

    dEFCJU.png

    import React, { Component } from 'react';
    export default class App extends Component {
      constructor() {
        super();
        console.log('父组件的constructor');
        this.state = {
          message: 'hello world',
          num: 10,
        }
      }
      render() {
        console.log('父组件的render');
        const { message } = this.state;
        return (
          <div>
            <h3>{message}</h3>
            <button onClick={() => this.changeMessage()}>验证生命周期</button>
          </div>
    
        )
      }
      changeMessage() {
        this.setState({
          message: 'hello react'
        })
      }
      
      componentDidMount() { //cdm
        console.log('只会初始化时候执行唯一一次componentDidMount()');
        console.log('constructor()函数执行->render()函数执行->componentDidMount()函数执行');
      }
      componentDidUpdate(prevProps, prevState) { //cdu
        console.log(prevProps); //{}
        console.log(prevState); //第一次点击{ message: 'hello world', num:10,}
        console.log('componentDidUpdate初始化时不会执行,只有在state中数据发生改变时执行');
        console.log('render()函数执行->componentDidUpdate()函数执行');
      }
    
      componentWillUnmount(){
        console.log('组件卸载时候触发');
      }
    
    
    }
    
    

    生命周期函数

    componentDidMount

    可以进行的数据操作有

    1.真实dom操作,在ReactDOM.render()执行之后调用componentDidMount。这个时候虚拟dom已经渲染成真实的DOM
    2. 网络请求,改变state里面数据
    3. 添加一些订阅 (在componentWillUnmount手动取消订阅)

    componentDidMount() {
        fetchPosts().then(response => {
          this.setState({
            posts: response.posts
         });//重新执行render函数,生成虚拟dom。
    });
    

    componetDidUpdate(prevProps,prevState)

    组件初始化不会执行,只有当数据改变后才会执行。

    可以进行的数据操作有

    1. 真实dom操作
    2. 通过前后props|state变化,判断是否发生网络请求。
    componentDidUpdate(prevProps) {
      // 典型用法(不要忘记比较 props):
      if (this.props.userID !== prevProps.userID) {
        this.fetchData(this.props.userID);
      }
    }
    

    componentWillUnmount()

    组件卸载和销毁之前调用。

    可以进行的数据操作有

    1. 清除定时器
    2. 清除componentDidMount里面订阅的事件

    数组通信 &&propTypes

    rpcp快捷键

    https://react.docschina.org/docs/typechecking-with-proptypes.html

    //app.jsx
    import React, { Component } from 'react';
    import Header from './component/appComp/Header'
    class App extends Component {
      constructor(props) {
        super(props)
        this.state = {
          headerMessage: 'hello react',
          headerMessage1: 100,
    
        }
      }
    
      render() {
        return (
          <div>
            <h2>app</h2>
            <Header
              headerMessage={this.state.headerMessage}
              headerMessage1={this.state.headerMessage1}
              changeHeaderMessage={(newMessage) => { this.changeHeaderMessage(newMessage) }}>
            </Header>
          </div>
        );
      }
      changeHeaderMessage(newMessage) {
        this.setState({
          headerMessage: newMessage
        })
      }
      
    }
    
    export default App;
    
    
    //Header.jsx子组件 rpcp
    import React, { Component } from 'react'
    import propTypes from 'prop-types'
    
    export default class Header extends Component {
      //如果夫组件没有传属性,可以允许使用默认值。如果这个是属性是isRequired,但是父组件也没有传,如果设置了该值是默认值也不会有警告
      static defaultProps = {
        headerMessage: 'default value headerMessage ' //给headerMessage设置默认值
      }
      static propTypes = {
        headerMessage: propTypes.string.isRequired, //string类型必选
        headerMessage1: propTypes.oneOfType(
          [propTypes.string, propTypes.number.isRequired] //任意类型之一,并且number必传
        ),
        changeHeaderMessage: propTypes.func //函数类型
      }
      constructor(props) {
        super(props)
        this.state = {
    
        }
      }
    
      render() {
        return (
          <div>
            <div className="header">{this.props.headerMessage}</div>
            <div>{this.props.headerMessage1}</div>
    		//子组件给父组件传值。通过函数
            <button onClick={() => { this.props.changeHeaderMessage('hello world') }}>改变父组件的header值</button>
          </div>
        )
      }
    }
    
    

    本质是父组件传函数给子组件。子组件调用父组件函数时候会传递过来一些值。

    deGE4K.png

    react实现Vue插槽效果

    首先明确一点:react没有插槽,需要自己手动实现

    插槽:父组件向子组件传递HTML结构。props是向子组件传递数据。

    1.this.props.children实现插槽

    duSKOJ.png

    2.this.props直接实现插槽

    综合建议:多多使用方式二 this.props实现插槽效果。

    duC8kn.png

    跨组件通信

    app组件->子组件Profile->孙组件ProfileHeader.想要从app组件传值到孙组件ProfileHeader,就要通过props属性先传给子组件,然后子组件在做一次Props转发到子组件。这就是一层一层传递数据。

    dudT54.png

    Context API

    Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

    Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。

    React.createContext

    const MyContext = React.createContext(defaultValue);
    

    Context.Provider

    <MyContext.Provider value={/* 某个值 */}>
    

    每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。

    当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染

    多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。、

    Class.contextType

    和 static contextType = MyContext;等价.给this.context赋值操作。

    duRzZj.png

    孙组件同样可以改变爷组件的值。传递函数

    duImPs.png

    跨组件事件events

    yarn add events

    d63i9g.png

    ref

    ref等于字符串|对象|函数。但是ref=字符串会被react抛弃。不建议使用

    import React, { PureComponent } from 'react'
    
    export default class App extends PureComponent {
      render() {
        return (
          <div>
            //ref="字符串"
            <h3 ref="titleRef">hello world!</h3>
            <button onClick={e => this.changeText()}>ref改变文本</button>
          </div>
        )
      }
      changeText() {
          //获取
        this.refs.titleRef.innerHTML='hello react'
      }
    
    
    }
    
    import React, { PureComponent, createRef } from 'react'
    export default class App extends PureComponent {
      constructor(props) {
        super(props)
        this.titleRef = createRef()
    
        this.state = {
    
        }
      }
    
      render() {
        return (
          <div>
            //ref=一个由createRef函数返回的对象。
            <h3 ref={this.titleRef}>hello world!</h3>
            <button onClick={e => this.changeText()}>ref改变文本</button>
          </div>
        )
      }
      changeText() {
        this.titleRef.current.innerHTML ='hello react'
      }
    
    
    }
    
    
    import React, { PureComponent } from 'react'
    export default class App extends PureComponent {
      constructor(props) {
        super(props)
        this.titleEl = null
    
        this.state = {
    
        }
      }
    
      render() {
        return (
          <div>
            {/* args是h3dom元素,并且函数在初始化时候就执行 */}
            <h3 ref={args => this.titleEl = args}>hello world</h3>
            <button onClick={e => this.changeText()}>ref改变文本</button>
          </div>
        )
      }
      changeText() {
        this.titleEl.innerHTML = 'hello react'
      }
    
    
    }
    
    

    dcBc7D.png

    ref转发forwardRef

    
    // 不能再函数式组件上面使用ref 。 要想获取函数式里面的元素 用forwardRef
    const SS2=forwardRef(function  (props,ref) {
      return (
        <div>
          <h6 ref={ref}>ss2--{props.a}</h6>
        </div>
      )
    })
    export default class Son6 extends PureComponent {
      constructor(props) {
        super(props)
        this.h4Ref = createRef()
        this.ss1Ref=createRef() 
        this.ss2Ref=createRef()
    
      }
      render() {
        return (
          <div>
            <h4 ref={this.h4Ref}>son6</h4>
            <SS1 ref={this.ss1Ref}></SS1>
            <SS2  ref={this.ss2Ref} a='10'></SS2>
            <button onClick={e=>this.showRef()}>打印ref</button>
          </div>
        )
      }
      showRef(){
        console.log(this.h4Ref.current);
        console.log(this.ss1Ref.current);
        console.log(this.ss2Ref.current);
        
      } 
    }
    
    

    setState

    react里面没有Vue2的Object.defineProperty或者Vue3 proxy监视数据变化。

    必须手动调用setState来告知React数据已经发生了改变。

    setState是继承过来的方法。

    setState异步更新

    为什么setState是异步更新?

    1. 设计为异步,可以显著提高性能。
      1. 如果设计为同步,每次调用setState都会触发调用render函数,界面将会重新渲染,最好的办法是获取到多个更新,批量更新
    2. 如果同步更新state,但是还没有执行render函数,render里面有子组件,子组件通过props传值。可能state和props不能保持同步(数据一致性)

    获取到异步更新的数据

    两种方式拿到最新的数据:

    1. setState({},function)回调函数里面拿到最新数据
    2. componentDidUpdate生命周期函数里面拿到最新数据

    d8Dc26.png

    设计setState为同步更新方案

    setState放入定时器中或者原生DOM事件中,setState将会变为同步

    1将setState放入到定时器中执行,setState将会变为同步

     render() {
        return (
          <div>
            <h3>{this.state.counter}</h3>
            <button onClick={e => this.increment()}>+1</button>
          </div>
        )
      }
      increment() {
        setTimeout(() => {
          this.setState({ counter: this.state.counter + 10 })
          console.log(this.state.counter);
        }, 0)
      }
    

    2.在dom原生事件中,setState是同步。

    render() {
        console.log('render');
        return (
          <div>
            <h3>{this.state.counter}</h3>
            {/* <button onClick={e => this.increment()}>+1</button> */}
            <button id='btn'>+1</button>
          </div>
        )
      }
      componentDidMount() {
        //原生事件方式1
        document.getElementById('btn').onclick = () => {
          this.setState({ counter: this.state.counter + 10 })
          console.log(this.state.counter);
        }
        // 原生事件方式2
        document.getElementById('btn').addEventListener('click', () => {
          this.setState({ counter: this.state.counter + 10 })
          console.log(this.state.counter);
    
        })
      }
    

    setState数据的合并

    state里面数据是合并的。源码是Object.assign({},this.state,新传入的对象)

    setState本身的合并

    setState本身合并导致的现象:

    render函数只会执行一次。componentDidUpdate回调函数也只会执行一次。

    import React, { Component } from 'react';
    class App extends Component {
      constructor(props) {
        super(props)
    
        this.state = {
          num: 10,
          message: 'hello world'
        }
      }
      render() {
        console.log(111);
        return (
          <div>
            <h3>{this.state.num}</h3>
            <button onClick={e => this.changeNum()}>改变num</button>
          </div>
        );
      }
      // setState会被合并,render函数只会执行一次,但是state里面的num变为10+40=50 message变为hello react
      changeNum() {
        this.setState({ num: this.state.num + 10 })
        this.setState({ num: this.state.num + 20 })
        this.setState({ num: this.state.num + 30 })
        this.setState({ num: this.state.num + 40 })
        this.setState({ message: 'hello react' })
    
      }
    }
    export default App;
    
    

    setState不让它本身合并

    setState(function)传入函数。注意这个也是异步。

    render函数也只会执行一次。componentDidUpdate回调函数也只会执行一次。

    但是num是10+10+20+30=70

    import React, { Component } from 'react';
    class App extends Component {
      constructor(props) {
        super(props)
    
        this.state = {
          num: 10,
          message: 'hello world'
        }
      }
      render() {
        console.log(111);
        return (
          <div>
            <h3>{this.state.num}</h3>
            <button onClick={e => this.changeNum()}>改变num</button>
          </div>
        );
      }
      // setState会被合并,render函数只会执行一次,但是state里面的num变为10+40=50 message变为hello react
      changeNum() {
        this.setState((preState) => {
          console.log(preState);
          return {
            num: preState.num + 10
          }
    
        })
        this.setState((preState) => {
          console.log(preState);
          return {
            num: preState.num + 20
          }
    
        })
        this.setState((preState) => {
          console.log(preState);
          return {
            num: preState.num + 30
          }
    
        })
    
    
      }
    }
    export default App;
    
    

    setState传递的是不可变数据

    换言之:setState不要改变原始this.state里面的数据。

    this.setState()函数执行就会触发shouldComponentUpdate函数执行,只有shouldComponentUpdate函数返回true才会去执行render函数。shouldComponentUpdate返回false,render函数就不会被执行。默认情况下shouldComponentUpdate返回true 所以可以省略直接执行render函数.

    
      shouldComponentUpdate(nextProps, nextState) {
       
        if (nextState.friends !== this.state.friends) {
          return true //只有返回true 才会去执行render函数
        }
        return false
      }
    

    React更新机制

    dGZD0J.png

    dGWdn1.png

    dJCdWF.png

    dJPjN6.png

    render函数

    dJZQrF.png

    import { PureComponent } from 'react'

    React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。如果赋予 React 组件相同的 props 和 state,render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。

    dJ1MMn.png

    dJ8bKs.png

    PureComponent&&memo

    PureComponent对新旧state props进行浅层比较。如果没发生变化,就不会调用render函数

    function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
     
      // PureReactComponent 自带isPureReactComponent属性为true
      if (ctor.prototype && ctor.prototype.isPureReactComponent) {
        return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState); //结果返回false 表示不更新  即render函数不会执行
      }
    
      return true;
    }
    
    
    
    
    function shallowEqual(objA, objB) {
      if (Object.is(objA, objB)) { //setState会返回一个新的对象 所以基本上 这个判断进不去
        return true;
      }
      
      if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
        return false;
      }
    
      var keysA = Object.keys(objA);
      var keysB = Object.keys(objB);
      // 说明setState如果加了新属性或者减少 PureComponent也是会更新的
      if (keysA.length !== keysB.length) {
        return false; 
      } // Test for A's keys different from B.
    
    
      for (var i = 0; i < keysA.length; i++) {
        // 如果新的没有旧的key 或者新的旧的key对应的value不一样 也是会更新的 比较地址值 
        //注意点2: 只是对第一层数据的key进行比较,
        if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
          return false;
        }
      }
    
      return true;
    }
    

    受控组件和非受控组件

    受控组件

    在 HTML 中,表单元素(如<input><textarea><select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。

    我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”

    大白话:由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。

    dcLRL6.png

    dcx7rR.png
    dgFK9P.png

    多输入
    dgQC9g.png

    高阶函数

    知识待补充。。。

    高阶组件HOC

    higher-order Component 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

    高阶组件:高阶组件的参数是组件,返回值是新组件的函数。

    注意:高阶组件本身不是一个组件而是一个函数。但是这个函数的参数是一个组件A,返回值也是一个组件B。组件B是组件A的父组件。

    const EnhancedComponent = higherOrderComponent(WrappedComponent);
    

    dR9Mw9.png

    HOC高阶组件

    本质就是一个函数highOrderComponent。入参是组件,返回值也是组件。起到了组件劫持的效果

    const EnhanceComponent=highOrderComponent(WrapperComponent)
    
    
    
    // hoc告诫组件定义形式1 类组件
    function enhance(Wrapper) {
      return class EnhanceComponent extends PureComponent {
        render(){
          return (
            <div>
              <Wrapper {...this.props} region='中国'></Wrapper>
            </div>
          )
        }
      }
    }
    
    //函数式组件 更清爽不是
    function enhance1(Wrapper) {
      return function EnhanceComponent(props) { //进化为箭头函数
        return <Wrapper {...props} region='中国'></Wrapper>  //实现props属性增强。
      }
    }
    //函数式组件 变形 箭头函数形式
    function enhance2(Wrapper){
        return props=>{
            return <Wrapper {...props} regin='中国'></Wrapper>
        }
    }
    

    简化Context

    原始写法。出现的问题是 Son3|Son4里面代码高度重复。

    //Fa.jsx
    <UserContext.Provider value={{nickName:'zs',level:90,region:'中国'}} >
    
              <Son3  ></Son3>
              <Son4 ></Son4>
    
     </UserContext.Provider>
    
    
    class Son3 extends PureComponent {
      render() {
        // UserContext.Consumer 比 ContextType 适用性更强  因为UserContext.Consumer在函数式组件和类组件里面都可以使用
        return <UserContext.Consumer>
          {value => {
            return (
              <div>
                <h4>son3 --{value.nickName}---{value.level}---{value.region}</h4>
    
              </div>
            )
          }}
        </UserContext.Consumer>
        
      }
    }
    
    class Son4 extends PureComponent {
      render() {
        return (
          <UserContext.Consumer>
            {
              user => {
                return (<div>
                  <h4>son4 --{user.nickName}---{user.level}---{user.region}</h4>
                </div>)
              }
            }
          </UserContext.Consumer>
    
        )
      }
    }
    

    Hoc增强做法

    //fa.jsx 这个不变 
    <UserContext.Provider value={{nickName:'zs',level:90,region:'中国'}} >
    
              <Son3  a='1' b='2' ></Son3>
              <Son4 ></Son4>
    
      </UserContext.Provider>
    //精华在这里。 定义一个高阶组件withUser函数************************************************
    export function withUser(Wrapper) {
      return props => { //返回的是一个函数式组件
        return <UserContext.Consumer>
          {value => {
            //这里看清楚 是把value属性展开 然后当作props属性来传递的 ==可以的==
            return <Wrapper {...props} {...value}></Wrapper>
          }}
        </UserContext.Consumer>
      }
    }
    
    //use 
    class Son3 extends PureComponent {
      render() {
        return (
          <div>
                //和普通组件使用的毫无感知一摸一样 ,也是props属性增强 写出更加优化的代码。
             <h4>son3 --{this.props.nickName}---{this.props.level}---{this.props.region}</h4>
          </div>
        )
        
      }
    }
    export default withUser(Son3) // 高阶函数调用*****************************88
    
    
    

    鉴权

    判断是否有权限进来这个页面。目的就是为了使用更加优雅 <Son5 isLogin={this.state.isLogin}></Son5>

    
    class LoginPage extends PureComponent {
      render() {
        return (
          <div>
            <h4>去登录页面</h4>
          </div>
        )
      }
    }
    
     class Son5 extends PureComponent {
      render() {
        return (
          <div>
            <h4>son5</h4>
          </div>
        )
      }
    }
    // use  <Son5  isLogin={this.state.isLogin}></Son5>
    function withAuth(Wrapper) {
      return props => { //props就是外面传的自定义属性
        const isLogin = props.isLogin;
        if (isLogin) return <Wrapper {...props}></Wrapper>
        else return <LoginPage></LoginPage>
    
    
      }
    }
    export default withAuth(Son5)
    

    react-css

    内联样式

    dRft2V.png

    内联样式优点:不同组件之间,样式无论如何都不会发生冲突。可以动态获取到state中的状态。

    缺点:写法上需要使用驼峰标识;大量内联样式出现代码混论;伪类和伪元素无法编写

    img

    img

    css module

    解决各个模块之间,样式不冲突问题。
    但是最大的局限是:不能动态使用state状态值。

    注意事项:文件命名必须是xxx.module.css;

    dRvDd1.png

    dRzA9f.png

    styled-components

    函数调用标签模板字符串

    const name = 'zs'
    const age = 10
    const message = `my name is ${name}, age is ${age}`
    console.log(message);
    
    // 标签模板字符串 函数调用标签模板字符串
    function foo(...args) {
      console.log(args);
    
    }
    foo`${message}` //[ [ '', '' ], 'my name is zs, age is 10' ]
    foo`my name is ${name}, age is ${age}` //[ [ 'my name is ', ', age is ', '' ], 'zs', 10 ]
    foo`
     font-size:12px;
     color:${props=>props.color};
    `
    

    css-in-js一种流行库 yarn add styled-components

    styled.标签 本质是函数执行 ,返回值是react组件 并且这个组件自带了一些样式 。

    dhkb4g.png

    dhEUyR.png

    dhKFje.png

    继承(不多)

    d4cUQf.png

    设置共享样式主题

    也就是一些公共样式放在一起
    d4RSzt.png
    动态添加className
    d4WWC9.png

    Ant Design of React(antd)

    yarn add antd

    按需加载

    antd 的 JS 代码默认支持基于 ES modules 的 tree shaking。对于js部分,直接引入 import {Button,DataPicker} from "antd"就有按需加载的效果。

    craco

    https://ant.design/docs/react/use-with-create-react-app-cn

    yarn run eject会暴露出来webpack配置进行修改。其实在开发中不建议直接修改webpack配置信息。

    yarn add @craco/craco

    修改package.json srcipt脚本文件

    "scripts": {
        "start": "set PORT=3000 && craco start",
        "build": "set GENERATE_SOURCEMAP=false && craco build",
        "test": "craco test"
      },
    

    修改antd内置主题颜色

    1.安装 yarn add craco-less

    2.新建 craro.config.js

    const CracoLessPlugin = require('craco-less');
    module.exports = {
      plugins: [
        {
          plugin: CracoLessPlugin,
          options: {
            lessLoaderOptions: {
              lessOptions: {
                modifyVars: { '@primary-color': '#1DA57A' },
                javascriptEnabled: true,
              },
            },
          },
        },
      ],
    
    }
    

    3.修改主入口文件index.js

    // import 'antd/dist/antd.css';
    import 'antd/dist/antd.less';
    

    修改项目的别名

    //craco.config.js
    const CracoLessPlugin = require('craco-less');
    const path = require('path');
    const resolve = dir => path.resolve(__dirname, dir)
    
    module.exports = {
      plugins: [
        {
          plugin: CracoLessPlugin,
          options: {
            lessLoaderOptions: {
              lessOptions: {
                modifyVars: { '@primary-color': '#1DA57A' },
                javascriptEnabled: true,
              },
            },
          },
        },
      ],
      webpack: {
        alias:{
          "@":resolve("src"),
          "components":resolve("src/components"),
          "assets":resolve("src/assets"),
          "pages":resolve("src/pages")
    
    
    
        }
    
      }
    };
    

    动画

    yarn add react-transition-group 
    

    react-transition-group主要包含4个内置组件

    Transition:用的不多

    CSSTransition 过渡效果

    SwitchTransition 两个组件的显示和隐藏切换效果

    TransitionGroup包裹多个组件

    CSSTransition:淡入淡出效果

    classNames:自定义类名

    unmountOnExit:隐藏是否卸载组件,默认不会卸载,

    appear:首次进入是否加入动画,默认是不加的,其实不加动画也可以

    render() {
        return (
          <div>
            <Button type="primary" onClick={e => this.change()}>显示/隐藏(透明度opacity改变 scale大小缩放)</Button>
    
            <CSSTransition in={this.state.isShow} classNames='demo-card' timeout={300} unmountOnExit={true} appear>
              <Card
                style={{  300 }}
                cover={
                  <img
                    alt="example"
                    src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png" />
                }
                actions={[
                  <SettingOutlined key="setting" />,
                  <EditOutlined key="edit" />,
                  <EllipsisOutlined key="ellipsis" />,
                ]}
              >
                <Meta
                  avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
                  title="Card title"
                  description="This is the description"
                />
              </Card>
    
            </CSSTransition>
            <div>hhhaha</div>
          </div>
        )
      }
    
    //备注 transform-origin: 50% 50%;默认scale缩放是以中心点。但是根据效果可以改比如 transform-origin:0 0;以左上角缩放
    .demo-card-enter,.demo-card-appear{
      opacity: 0;
      transform: scale(.6);
    }
    .demo-card-enter-active,.demo-card-appear-active{
      opacity: 1;
      transition: opacity 300ms, transform 300ms;
      transform: scale(1);
    
    }
    .demo-card-enter-done,.demo-card-appear-done{
    
    
    }
    
    .demo-card-exit{
      opacity: 1;
      transform:scale(1);
    
    }
    .demo-card-exit-active{
      opacity: 0;
      transform:scale(.6);
      transition: opacity 300ms, transform 300ms;
    }
    .demo-card-exit-done{
      opacity: 0;
    }
    

    SwitchTransition:组件来回切换效果

    SwitchTransition要配合CSSTransition使用。并且使用key

    //isOn:true
    render() {
        return (
          <div>
            <h4>switchTransition</h4>
            <span>nihioa</span>
            <SwitchTransition mode="out-in">
              <CSSTransition key={this.state.isOn ? 'on' : 'off'} classNames='btn' timeout={600}>
                <Button type="primary"
                  onClick={e => this.setState({ isOn: !this.state.isOn })}
                >{this.state.isOn ? 'on' : 'off'}</Button>
              </CSSTransition>
            </SwitchTransition>
    
          </div>
        )
      }
    
    //SwichTransition效果1
    
    .btn-enter{
      opacity: 0;
    }
    .btn-enter-active{
      opacity: 1;
      transition:opacity 600ms;
    }
    
    .btn-exit{
      opacity: 1;
    }
    .btn-exit-active{
      opacity: 0;
      transition:opacity 600ms;
    }
    
    
    //SwitchTransition效果2
    .btn-enter{
      opacity: 0;
      transform:translateX(100%)
    }
    .btn-enter-active{
      opacity: 1;
      transform:translateX(0);
      transition:opacity 600ms,transform 600ms;
    }
    
    .btn-exit{
      opacity: 1;
      transform:translateX(0)
    }
    .btn-exit-active{
      opacity: 0;
      transform:translateX(-100%);
      transition:opacity 600ms,transform 600ms;
    }
    
    

    TransitionGroup

    TransitionGroup添加列表动画

    export default class Son3 extends PureComponent {
      constructor(props) {
        super(props)
        this.state = {
          list: ['zs', 'lisi', 'wangwu']
        }
      }
      render() {
        return (
          <div>
            <h4>son3</h4>
            <button onClick={e => this.addNames()}>addName</button>
              <TransitionGroup>
                {this.state.list.map((v, i) => {
                  return (
                    <CSSTransition key={i} timeout={300} classNames='demo-item'>
                      <div>{v}</div>
                    </CSSTransition>
                  )
                })}
              </TransitionGroup>
          </div>
        )
      }
      addNames() {
        this.setState({
          list: [...this.state.list, 'shunzi']
        })
      }
    }
    
    .demo-item-enter {
      opacity: 0;
      transform: scale(.6);
      transform-origin:0 0;
      
    
    }
    
    .demo-item-enter-active {
      opacity: 1;
      transform: scale(1);
      transition: opacity 300ms, transform 300ms;
      transform-origin:0 0;
    
    
    }
    
    .demo-item-enter-done {
      /* color:red; */
    }
    
    .demo-item-exit {
      opacity: 1;
      transform: scale(1)
    }
    
    .demo-item-exit-active {
      opacity: 0;
      transform: scale(.6);
      transition: opacity 300ms, transform 300ms;
    }
    
    .demo-item-exit-done {
      opacity: 0;
    }
    

    纯函数

    纯函数:函数返回值只依赖它的参数(不依赖外部变量),并且在函数执行过程中没有任何的副作用。

    副作用:是说函数在执行过程中产生了外部可观察变化。

    比如:发送HTTP请求;操作DOM;修改外部数据;console.log()输出打印;调用Date.now和Math.random函数。

    所有的React组件都必须像纯函数一样,保护它们的props不被修改(不能修改外部数据)。

    redux

    redux最基本的使用

    redux最基本的使用,已经实现了核心 dispatch->reducer->subscribe(callback)

    // import redux from 'redux';
    import { createStore } from 'redux'
    
    const initialState = {
      counter: 0
    }
    // reducer  很类似reduce函数啊
    function reducer(state = initialState, action) { //state给个默认值
      switch (action.type) {
        case 'INCREMENT':
          return { ...state, counter: state.counter + 1 } //返回新的state
        case 'DECREMENT':
          return { ...state, counter: state.counter - 1 }
        case 'ADD_NUMBER':
          return { ...state, counter: state.counter + action.num }
        case 'SUB_NUMBER':
          return { ...state, counter: state.counter - action.num }
    
        default:
          return state //如果没有匹配就把state原路返回
      }
    
    }
    
    const store = createStore(reducer)
    
    //订阅在定义dispatch之前   dispatch->reducer->subscribe(callback)
    store.subscribe(() => {
      console.log(store.getState());
      console.log(store.getState().counter);
      console.log('------------------------------');
    })
    
    
    //定义action action是普通对象
    const action1 = { type: 'INCREMENT' } //递增1
    const action2 = { type: 'DECREMENT' } //递减1
    const action3 = { type: 'ADD_NUMBER', num: 10 }
    const action4 = { type: 'SUB_NUMBER', num: 20 }
    
    //每次派发action,都会触发reducer函数
    store.dispatch(action1) //执行reducer
    store.dispatch(action2)
    store.dispatch(action3)
    store.dispatch(action4)
    
    
    

    redux目录划分

    redux在react里面的简单使用

    import React, { PureComponent } from 'react'
    import store from './store'
    import { subAction } from './store/actionCreators'
    export default class S2 extends PureComponent {
      constructor(props) {
        super(props)
        this.state = {
          counter: store.getState().counter //绑定store里面的值
        }
      }
      render() {
        return (
          <div>
            <h4>son2</h4>
            {/* 改变store里面的值 */}
            <button onClick={e => store.dispatch(subAction(20))}>-20</button>
            <h4>{this.state.counter}</h4>
          </div>
        )
      }
    
      componentDidMount() {
        // store里面的值一旦改变,subscribe里面回调函数执行。执行调用setState保持与store值同步
        this.unsubscribe = store.subscribe(() => {
          this.setState({
            counter: store.getState().counter
          })
        })
      }
      componentWillUnmount() {
        this.unsubscribe() //取消订阅 返回一个函数
      }
    }
    
    

    封装connect函数简化代码

    //connect.js 
    import { PureComponent } from "react"
    import store from '../08redux/store'
    //react 和connect连接在一起
    export function connect(mapStateToProps, mapDispatchToProps) {
      return function enhanceHOC(Wrapper) {
        return class extends PureComponent {
          constructor(props) {
            super(props)
            this.state = {
              storeState: mapStateToProps(store.getState())
            }
          }
          render() {
            return (
              <Wrapper
                {...this.props}
                {...mapStateToProps(store.getState())}
                {...mapDispatchToProps(store.dispatch)} ></Wrapper>
            )
          }
          componentDidMount() {
            this.unsubscribe = store.subscribe(() => {
              this.setState({
                storeState: mapStateToProps(store.getState())
              })
    
            })
    
          }
          componentWillUnmount() {
            this.unsubscribe()
          }
        }
      }
    }
    
    //use connect
    import React, { PureComponent } from 'react'
    import { connect } from '../utils/connect'
    import { addAction } from './store/actionCreators'
    
    function S1(props) {
      return (
        <div>
          <h4>son1</h4>
          <h4>'state.counter'--????{props.counter}</h4>
          <button onClick={e => props.addTen(10)}>+10</button>
        </div>
      )
    }
    
    const mapStateToProps = state => {
      return {
        counter: state.counter
      }
    }
    const mapDispatchToProps = dispatch => ({ //return简写方式 还是返回一个对象而已
      addTen(num) {
        dispatch(addAction(num))
      }
    })
    export default connect(mapStateToProps, mapDispatchToProps)(S1) //重点是这里
    
    

    react-redux

    //1使用Provider
    import store from './08redux/store'
    import { Provider } from 'react-redux'
    import App from './08redux/Fa'
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
    
      document.getElementById('root')
    );
    
    
    
    //2
    import React, { PureComponent } from 'react'
    // import { connect } from '../utils/connect' //把自己写的注释掉
    import {connect} from 'react-redux' //直接用react-redux的connect函数 其他都不变
    import { addAction } from './store/actionCreators'
    
    function S1(props) {
      return (
        <div>
          <h4>son1</h4>
          <h4>'state.counter'--????{props.counter}</h4>
          <button onClick={e => props.addTen(10)}>+10</button>
        </div>
      )
    }
    
    const mapStateToProps = state => {
      return {
        counter: state.counter
      }
    }
    const mapDispatchToProps = dispatch => ({
      addTen(num) {
        dispatch(addAction(num))
      }
    })
    export default connect(mapStateToProps, mapDispatchToProps)(S1)
    

    redux结合异步操作

    中间件Middleware

    使用中间件:在redux的dispatch和reducer之间,扩展;一些额外的功能。比如异步数据接口,日志,添加代码调试逻辑等

    redux-thunk

    //store.js/index.js
    import { createStore, applyMiddleware } from 'redux'
    import thunkMiddleware from 'redux-thunk' //异步请求middleware
    import reducer from './reducer'
    // 中间件 applyMiddleware(中1,中2,中3)
    const storeEnhancer = applyMiddleware(thunkMiddleware)
    const store = createStore(reducer, storeEnhancer)
    
    export default store
    
    //store/actionCreators.js 定义getHomeMultiDataAction发送网络请求
    import axios from 'axios'
    //dispatch是redux-thunk传的参数
    export const getHomeMultiDataAction = (dispatch, getState) => {
      // console.log(getState()); 上一次的getState
      axios({
        url: 'http://123.207.32.32:8000/home/multidata'
      }).then(res => {
        const data = res.data.data
        dispatch(changeBannerAction(data.banner.list))
        dispatch(changeRecommendAction(data.recommend.list))
      })
    
    }
    
    //use
    import { getHomeMultiDataAction } from './store/actionCreators'
    //class组件
     componentDidMount() {
        this.props.getHomeMultiData()
        
      }
    
    const mapDispatchToProps = dispatch => ({
      //看这里
      getHomeMultiData(){
        dispatch(getHomeMultiDataAction) //看清楚 传入getHomeMultiDataAction函数,而不是将它执行
      }
    })
    export default connect(mapStateToProps, mapDispatchToProps)(S3)
    

    redux-devtools-extension

    调试工具而已

    //store.js
    import { createStore, applyMiddleware } from 'redux'
    import { composeWithDevTools } from 'redux-devtools-extension';
    import thunkMiddleware from 'redux-thunk' //异步请求middleware
    import reducer from './reducer'
    // 中间件 applyMiddleware(中1,中2,中3)
    const storeEnhancer = applyMiddleware(thunkMiddleware)
    const store=createStore(reducer,composeWithDevTools(storeEnhancer))
    
    export default store
    

    generator结合promise

    
    
    function* bar() {
      const result = yield new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(100);
        }, 3000)
      })
      console.log(result); //3000
    }
    const iter1 = bar()
    
    iter1.next().value.then(res => {
      iter1.next(res)
    })
    
    

    redux-saga 异步请求

    其实也是拦截dispatch->reducer中间的步骤加入redux-sage的逻辑

    //store.js
    
    import { createStore, applyMiddleware } from 'redux'
    import { composeWithDevTools } from 'redux-devtools-extension';
    import createSagaMiddleware from 'redux-saga' //异步请求middleware
    import reducer from './reducer'
    import saga from './saga'
    const sagaMiddleware = createSagaMiddleware()
    
    
    // 中间件 applyMiddleware(中1,中2,中3)
    const storeEnhancer = applyMiddleware( sagaMiddleware)
    const store = createStore(reducer, composeWithDevTools(storeEnhancer))
    
    sagaMiddleware.run(saga)
    
    export default store
    
    //cpnstansant.js
    export const FETCH_HOME_NUMTIDATA= 'FETCH_HOME_NUMTIDATA'
    
    //actionCreators
    import {  FETCH_HOME_NUMTIDATA } from './constants'
    export const fetchHomeMultiDataAction = {
      type: FETCH_HOME_NUMTIDATA
    }
    
    
    
    //saga.js
    //导出一个生成器函数
    import axios from 'axios'
    import { takeEvery, put, all, takeLatest } from 'redux-saga/effects'
    import { FETCH_HOME_NUMTIDATA } from './constants'
    import { changeBannerAction, changeRecommendAction } from './actionCreators'
    
    //自定义迭代器函数
    function* fetchHomeNumtiData(action) {
      const res = yield axios({
        url: 'http://123.207.32.32:8000/home/multidata'
      })
      const data = res.data.data
      yield all([
        yield put(changeBannerAction(data.banner.list)),
        yield put(changeRecommendAction(data.recommend.list))
        
      ])
    
    }
    //主函数
    export default function* saga() {
      yield takeEvery(FETCH_HOME_NUMTIDATA, fetchHomeNumtiData)
    
    
    }
    
    
    //use
    import { fetchHomeMultiDataAction } from './store/actionCreators'
    
    const mapDispatchToProps = dispatch => ({
      
      fetchHomeMultiData(){
        dispatch(fetchHomeMultiDataAction) 
      }
    })
    export default connect(mapStateToProps, mapDispatchToProps)(S3)
    
    
    componentDidMount() {
        this.props.fetchHomeMultiData()
        
      }
    
  • 相关阅读:
    返回一个随机数组中的子数组中的数相加最大的和
    四则运算二之结果
    四则运算二
    UVA 11741 Ignore the Blocks
    UVA 1408 Flight Control
    UVA 10572 Black & White
    CF1138D(545,div2) Camp Schedule
    UVA 1214 Manhattan Wiring
    UVA 11270 Tiling Dominoes
    BZOJ 3261 最大异或和
  • 原文地址:https://www.cnblogs.com/xiaoliziaaa/p/15339611.html
Copyright © 2020-2023  润新知