• React基础汇总


    基础

    创建项目

    借助脚手架,新建一个React项目

    npx create-react-app 项目名

    • create-react-app是React脚手架的名称

    启动项目

    npm start 或者 yarn start

    目录
    • src是源文件
    • index.js相当于Vue的main.js文件。整个程序的入口
    • App.js相当于Vue的App.js,根组件
    {}表达式
    • 里面可以写入方法,变量和三元等js表达式
    • 表达式是可以产生一个值的js语句
    import { useState } from 'react';
    import './App.css';
    
    const App = () => {
      const [num, setNum] = useState(100)
      const [flag, setFlag] = useState(true)
      const fn = () => {
        return '方法执行了'
      }
    
      return (
        <div className="App">
          <h3>{ }中可以使用定义好的变量</h3>
          { num }
          <h3>{ }中可以使用三元表达式</h3>
          { flag ? num : '无' }
          <h3>{ }中可以使用短路与</h3>
          { flag && num }
          <h3>{ }中可以使用短路或</h3>
          { !flag || num }
          <h3>{ }中可以使用方法</h3>
          { fn() }
        </div>
      );
    }
    
    export default App;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    列表渲染
    • 通过map进行遍历,里面需要绑定key值,方便diff算法进行对比,提高diff性能
    • 重复渲染那个模板,就return
    • key 在当前列表中要唯一的字符串或者数值(String/Number)
    export default function App() {
      const [list, setList] = useState([
        { id: 0, name: '张三' },
        { id: 1, name: '李四' },
        { id: 2, name: '王五' },
      ])
      return (
        <div>
          {list.map((item) => (
            <p key={item.id}>姓名:{item.name}</p>
          ))}
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    条件渲染
    • 根据是否满足条件生成HTML结构,比如Loading效果
    • 可以使用 三元运算符逻辑与(&&)运算符逻辑或(||)运算符
    export default function App() {
      const [flag, setFlag] = useState(true)
      return (
        <div>
          {flag ? '正确的' : null}
          {flag && '前面只有为true的情况下才会执行'}
          {!flag || '前面只有为false的情况下才会执行'}
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    if系列判断渲染

    • 可以声明一个方法,接收值,内部进行判断,并返回对应的结果
    export default function App() {
      const getType = (type) => {
        if (type === 0) {
          return <span>111</span>
        } else if (type === 1) {
          return <span>222</span>
        } else if (type === 2) {
          return <span>333</span>
        }
      }
      return (
        <div>
          {getType(1)}
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    样式处理
    • 利用className指定类名,适合样式比较多的情况

    • 直接行内样式,适合样式比较少的

    <h2 style={{ color: 'red' }}>标题</h2>
    
    • 1
    • 单独声明一个样式类名对象
    export default function App() {
      let style = {
        color: 'pink',
        fontSize: 20,
      }
      return (
        <div>
          <h2 style={style}>标题</h2>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    动态类名

    • 根据条件显示类名
    export default function App() {
      let style = {
        color: 'pink',
        fontSize: 20,
      }
      let [flag, setFlag] = useState(true)
      return (
        <div>
          <h2 style={flag ? style : ''}>标题</h2>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    注意事项
    1. JSX必须有一个根节点,如果没有根节点,可以使用<>(幽灵节点)替代
    2. 所有标签必须形成闭合,成对闭合或者自闭合都可以
    3. JSX中的语法更加贴近JS语法,属性名采用驼峰命名法 class -> className for -> htmlFor
    4. JSX支持多行(换行),如果需要换行,需使用() 包裹,防止bug出现
    小案例1
    • 实现一个最基本的评论(不完整)
    import './index.css'
    import avatar from './images/avatar.png'
    // 依赖的数据
    const state = {
      // hot: 热度排序  time: 时间排序
      tabs: [
        {
          id: 1,
          name: '热度',
          type: 'hot',
        },
        {
          id: 2,
          name: '时间',
          type: 'time',
        },
      ],
      active: 'hot',
      list: [
        {
          id: 1,
          author: '刘德华',
          comment: '给我一杯忘情水',
          time: new Date('2021-10-10 09:09:00'),
          // 1: 点赞 0:无态度 -1:踩
          attitude: 1,
        },
        {
          id: 2,
          author: '周杰伦',
          comment: '哎哟,不错哦',
          time: new Date('2021-10-11 09:09:00'),
          // 1: 点赞 0:无态度 -1:踩
          attitude: 0,
        },
        {
          id: 3,
          author: '五月天',
          comment: '不打扰,是我的温柔',
          time: new Date('2021-10-11 10:09:00'),
          // 1: 点赞 0:无态度 -1:踩
          attitude: -1,
        },
      ],
    }
    // 时间格式化
    const format = (time) => {
      return `${time.getFullYear()}-${
        time.getMonth() + 1 < 10 ? '0' + time.getMonth() + 1 : time.getMonth() + 1
      }-${time.getDate() < 10 ? '0' + time.getDate() : time.getDate()} ${
        time.getHours() < 10 ? '0' + time.getHours() : time.getHours()
      }:${time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()}:${
        time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()
      }`
    }
    // tab切换
    const activeClick = (active) => {
      state.active = active
    }
    
    function App() {
      return (
        <div className="App">
          <div className="comment-container">
            {/* 评论数 */}
            <div className="comment-head">
              <span>5 评论</span>
            </div>
            {/* 排序 */}
            <div className="tabs-order">
              <ul className="sort-container">
                {state.tabs.map((item) => {
                  return (
                    <li
                      className={state.active === item.type ? 'on' : ''}
                      onClick={() => activeClick('hot')}
                      key={item.id}
                    >{item.name}排序
                    </li>
                  )
                })}
              </ul>
            </div>
    
            {/* 添加评论 */}
            <div className="comment-send">
              <div className="user-face">
                <img className="user-head" src={avatar} alt="" />
              </div>
              <div className="textarea-container">
                <textarea
                  cols="80"
                  rows="5"
                  placeholder="发条友善的评论"
                  className="ipt-txt"
                />
                <button className="comment-submit">发表评论</button>
              </div>
              <div className="comment-emoji">
                <i className="face"></i>
                <span className="text">表情</span>
              </div>
            </div>
    
            {/* 评论列表 */}
            <div className="comment-list">
              {state.list.map((item) => {
                return (
                  <div className="list-item" key={item.id}>
                    <div className="user-face">
                      <img className="user-head" src={avatar} alt="" />
                    </div>
                    <div className="comment">
                      <div className="user">{item.author}</div>
                      <p className="text">{item.comment}</p>
                      <div className="info">
                        <span className="time">{format(item.time)}</span>
                        <span
                          className={item.attitude === 1 ? 'like liked' : 'like'}
                        >
                          <i className="icon" />
                        </span>
                        <span
                          className={item.attitude === -1 ? 'hate hated' : 'hate'}
                        >
                          <i className="icon" />
                        </span>
                        <span className="reply btn-hover">删除</span>
                      </div>
                    </div>
                  </div>
                )
              })}
            </div>
          </div>
        </div>
      )
    }
    
    export default App
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141

    组件

    • 分为函数式组件(rfc)和类组件(rnc)
    • 安装ES7+ React/Redux/React-Native snippets这个插件后就可以使用上述指令快速创建组件
    • 主要讲函数式组件
    绑定事件
    • on开头,后面紧跟事件名(事件名首字母大写)

    on事件名 = { 事件处理函数名 } // 无参

    on事件名 = { () => 事件处理函数名(参数1,参数2…) } // 有参

    on事件名 = { (e) => 事件处理函数名(e,参数2…) } // 有参,带e的

    • 事件处理函数

    let/const 事件处理函数名 = (参数) => { … }

    eg:

    import React from 'react'
    
    export default function App() {
      const print = () => {
        console.log('无参的')
      }
      const hasParams = (e, num) => {
        console.log('有参的', e, num)
      }
    
      return (
        <div>
          <button onClick={print}>print</button>
          <button onClick={(e) => hasParams(e, '123')}>hasParams</button>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    小技巧
    • 数值改变
    • 数组添加
    • 对象修改
    import React from 'react'
    import { useState } from 'react'
    
    export default function App() {
      const [num, setNum] = useState(10)
      const [list, setList] = useState([])
      const [obj, setObj] = useState({
        name: '张三',
      })
    
      // 数值加几,可以直接在后面写加几
      const numAdd = (n) => {
        setNum(num + n)
      }
      // 数组添加,可以直接在尾部添加
      const listAdd = (item) => {
        setList([...list, item])
      }
      // 修改对象中的某一项
      const objEdit = (val) => {
        setObj({
          ...obj,
          // 下面的会覆盖上面的同名的属性,达到修改的目的
          name: val,
        })
      }
    
      return (
        <div>
          <button onClick={() => numAdd(1)}>数值加1--{num}</button>
          <div>
            <button onClick={() => listAdd('数组新的一项')}>数组添加一项</button>
            {list.map((item, i) => (
              <p key={i}>{item}</p>
            ))}
          </div>
          <div>
            <button onClick={() => objEdit('李四')}>
              修改对象的某一项(修改name)
            </button>
            <p>{obj.name}</p>
          </div>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 数组删除(最好利用filter)
    import React from 'react'
    import { useState } from 'react'
    
    export default function App() {
      const [list, setList] = useState([1,2,3])
      // 删除数组中下标为2的内一项
      const delItem = (index) => {
        let newList = list.filter((item, i) => i !== 2)
        setList(newList)
        // 或者直接操作,也是可以的
        // setList(list.filter((item, i) => i !== 2))
      }
    
      return (
        <div>
          <button onClick={() => delItem(2)}>删除数组中的某一项</button>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    受控组件
    • 被react状态控制的组件就叫受控组件。通过事件对象e,可以获取输入框中的值
    import React from 'react'
    import { useState } from 'react'
    
    export default function App() {
      const [val, setVal] = useState('')
      // 表单里面的值发生变化
      const onChange = (e) => {
        // 获得输入框中的值
        console.log(e.target.value)
        // 赋值给val
        setVal(e.target.value)
      }
    
      return (
        <div>
          <input type="text" name="" id="" value={val} onChange={onChange} />
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    非受控组件
    • 不受react状态控制的组件叫非受控组件。通过获取dom元素,来获取输入框中的值
    import React from 'react'
    import { useRef } from 'react'
    
    export default function App() {
      const ipt = useRef(null)
      // 表单里面的值发生变化
      const onChange = () => {
        // 获得输入框中的值
        console.log(ipt.current.value)
      }
    
      return (
        <div>
          <input type="text" name="" id="" ref={ipt} onChange={onChange} />
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    小案例2
    • 完整的评论功能
    import './index.css'
    import avatar from './images/avatar.png'
    import { useState } from 'react'
    
    // 时间格式化
    const format = (time) => {
      return `${time.getFullYear()}-${
        time.getMonth() + 1 < 10 ? '0' + time.getMonth() + 1 : time.getMonth() + 1
      }-${time.getDate() < 10 ? '0' + time.getDate() : time.getDate()} ${
        time.getHours() < 10 ? '0' + time.getHours() : time.getHours()
      }:${time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()}:${
        time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()
      }`
    }
    
    function App() {
      // hot: 热度排序  time: 时间排序
      const [tabs] = useState([
        {
          id: 1,
          name: '热度',
          type: 'hot',
        },
        {
          id: 2,
          name: '时间',
          type: 'time',
        },
      ])
      const [list, setList] = useState([
        {
          id: 1,
          author: '刘德华',
          comment: '给我一杯忘情水',
          time: new Date('2021-10-10 09:09:00'),
          // 1: 点赞 0:无态度 -1:踩
          attitude: 1,
        },
        {
          id: 2,
          author: '周杰伦',
          comment: '哎哟,不错哦',
          time: new Date('2021-10-11 09:09:00'),
          // 1: 点赞 0:无态度 -1:踩
          attitude: 0,
        },
        {
          id: 3,
          author: '五月天',
          comment: '不打扰,是我的温柔',
          time: new Date('2021-10-11 10:09:00'),
          // 1: 点赞 0:无态度 -1:踩
          attitude: -1,
        },
      ])
      // 切换的tab
      const [active, setActive] = useState('hot')
      // tab切换
      const activeClick = (type) => {
        setActive(type)
      }
      // 输入框的值
      const [iptVal, setIptVal] = useState('')
      // 得到输入框中的值
      const getVal = (e) => {
        setIptVal(e.target.value)
      }
      // 点击发送评论按钮
      const sendCommit = () => {
        if (!iptVal || iptVal.trim().length < 1) {
          return alert('输入不能为空或都是空格')
        }
        setList([
          ...list,
          {
            id: +new Date(),
            author: '孤勇者',
            comment: iptVal,
            time: new Date(),
            // 1: 点赞 0:无态度 -1:踩
            attitude: 0,
          },
        ])
        setIptVal('')
      }
      // 点击删除
      const delItm = (id) => {
        let newList = list.filter((item) => item.id !== id)
        setList(newList)
      }
      // 点击点赞/点踩
      const toggleMood = (item) => {
        let { id, attitude } = item
        let newList = list.map((item) => {
          if (item.id === id) {
            return {
              ...item,
              attitude: attitude === 1 ? 0 : 1,
            }
          } else {
            return item
          }
        })
        console.log(newList)
        setList(newList)
      }
    
      return (
        <div className="App">
          <div className="comment-container">
            {/* 评论数 */}
            <div className="comment-head">
              <span>{list.length} 评论</span>
            </div>
            {/* 排序 */}
            <div className="tabs-order">
              <ul className="sort-container">
                {tabs.map((item) => {
                  return (
                    <li
                      className={active === item.type ? 'on' : ''}
                      onClick={() => activeClick(item.type)}
                      key={item.id}
                    >{item.name}排序
                    </li>
                  )
                })}
              </ul>
            </div>
    
            {/* 添加评论 */}
            <div className="comment-send">
              <div className="user-face">
                <img className="user-head" src={avatar} alt="" />
              </div>
              <div className="textarea-container">
                <textarea
                  cols="80"
                  rows="5"
                  placeholder="发条友善的评论"
                  className="ipt-txt"
                  onChange={getVal}
                  value={iptVal}
                />
                <button className="comment-submit" onClick={sendCommit}>
                  发表评论
                </button>
              </div>
              <div className="comment-emoji">
                <i className="face"></i>
                <span className="text">表情</span>
              </div>
            </div>
    
            {/* 评论列表 */}
            <div className="comment-list">
              {list.map((item, index) => {
                return (
                  <div className="list-item" key={item.id}>
                    <div className="user-face">
                      <img className="user-head" src={avatar} alt="" />
                    </div>
                    <div className="comment">
                      <div className="user">{item.author}</div>
                      <p className="text">{item.comment}</p>
                      <div className="info">
                        <span className="time">{format(item.time)}</span>
                        <span
                          className={item.attitude === 1 ? 'like liked' : 'like'}
                          onClick={() => toggleMood(item)}
                        >
                          <i className="icon" />
                        </span>
                        <span
                          className={item.attitude === -1 ? 'hate hated' : 'hate'}
                        >
                          <i className="icon" />
                        </span>
                        <span
                          className="reply btn-hover"
                          onClick={() => delItm(item.id)}
                        >
                          删除
                        </span>
                      </div>
                    </div>
                  </div>
                )
              })}
            </div>
          </div>
        </div>
      )
    }
    
    export default App
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197

    组件通信

    • 父子通信,子父通信,非父子通信
    父->子通信
    • 父组件在子组件标签上绑定要传入的数据(会默认添加到props中),子组件通过props进行使用

    父组件

    import React from 'react'
    import { useState } from 'react'
    import Son from './pages/Son.jsx'
    
    export default function App() {
      const [num, setNum] = useState(100)
      return (
        <div>
          <h2>App</h2>
          <p>下面是子组件</p>
          <Son num={num}></Son>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    子组件

    import React from 'react'
    
    export default function Son(props) {
      return (
        <div>
          <h2>Son</h2>
          <p>从父组件传来的值是:{props.num}</p>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    props详解
    1. props是只读对象(readonly)

      • 是自顶向下单向数据流,根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改
    2. props可以传递任意数据

      • 数字、字符串、布尔值、数组、对象、函数(多用子向父传值)JSX(类似于插槽)

    父组件

    import React,{ useState }  from 'react'
    import Son from './pages/Son.jsx'
    
    export default function App() {
      // 数字
      const [num, setNum] = useState(100)
      // 字符
      const [str, setStr] = useState('str')
      // 布尔
      const [bool, setBool] = useState(false)
      // 数组
      const [list, setList] = useState([1, 2, 3])
      // 对象
      const [obj, setObj] = useState({ name: '张三', age: 24 })
      // 函数
      const print = () => {
        return 'print'
      }
      // jsx
      const jsx = <span>123</span>
    
      return (
        <div>
          <h2>App</h2>
          <p>下面是子组件</p>
          <Son
            num={num}
            str={str}
            bool={bool}
            list={list}
            obj={obj}
            print={print}
            jsx={jsx}
          >
            直接写在标签内的jsx结构,会自动传入到props中的children属性里(或者子组件标签上写children属性,一样的效果)
          </Son>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    子组件

    import React from 'react'
    // 也可以直接在参数这里解构
    export default function Son(props) {
      let { num, str, bool, list, obj, print, jsx } = props
      return (
        <div>
          <h2>Son</h2>
          <p>从父组件传来的值是:{num}</p>
          <p>从父组件传来的值是:{str}</p>
          <p>从父组件传来的值是:{bool ? '是true' : '是false'}</p>
          <p>从父组件传来的数组,渲染结果如下</p>
          <ul>
            {list.map((item) => (
              <li key={item}>{item}</li>
            ))}
          </ul>
          <p>从父组件传来的对象,渲染结果如下</p>
          {obj.name} --- {obj.age}
          <p>从父组件传来的函数,渲染结果如下</p>
          {print()}
          <p>父组件传来的jsx结构,如下</p>
          {jsx}
          <p>父组件传过来的jsx结构,如下</p>
          {props.children}
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    子->父通信
    • 也是通过props,通过传递函数进行通信
    • 子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数的实参

    子组件

    import React,{ useState }  from 'react'
    
    export default function Son(props) {
      let [msg, setMsg] = useState('子组件向父组件传递的数据')
      const sendMsg = () => {
        props.getMsg(msg)
      }
    
      return (
        <div>
          <h2>Son</h2>
          <button onClick={sendMsg}>点击向父组件传值</button>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    父组件

    import React,{ useState } from 'react'
    import Son from './pages/Son'
    
    export default function App() {
      let [sonData, setSonData] = useState({})
      const getMsg = (val) => {
        console.log(val)
        setSonData({ ...sonData, msg: val })
      }
    
      return (
        <div>
          <h2>App --- {sonData.msg}</h2>
          <Son getMsg={getMsg}></Son>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    兄弟组件通信
    • 通过状态提升,利用共同的父组件实现兄弟通信
    • 兄弟组件A -> 父组件 -> 兄弟组件B

    子组件A

    import React, { useState } from 'react'
    
    export default function SonA(props) {
      const [msgA, setMsgA] = useState('兄弟组件A传递的数据')
      const sendB = () => {
        props.getMsgA(msgA)
      }
    
      return (
        <div>
          <h3>Son1</h3>
          <button onClick={sendB}>点击发送给兄弟组件B</button>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    父组件

    import React, { useState } from 'react'
    import SonA from './pages/SonA'
    import SonB from './pages/SonB'
    
    export default function App() {
      const [msgA, setMsgA] = useState('')
      // 接收A组件传来的值
      const getMsgA = (val) => {
        setMsgA(val)
      }
    
      return (
        <div>
          <h2>App</h2>
          <SonA getMsgA={getMsgA}></SonA>
          <SonB msgA={msgA}></SonB>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    子组件B

    import React from 'react'
    
    export default function SonB(props) {
      return (
        <div>
          <h3>Son2</h3>
          <p>接收兄弟组件B传来的值为:{props.msgA}</p>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    跨组件通信Context
    • 直接在index.js文件中提供数据,则全局都可以使用
      • 适用于只是用1次的静态的数据
    • 如果提供的数据需要维护状态,则写到app.js

    使用步骤

    1. 首先创建一个独立的文件,Context.js
    import { createContext } from 'react'
    
    const Context = createContext()
    
    export default Context
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 那个组件需要就直接导入,Provider标签包裹根组件,value提供数据(提供的数据比较多,就可以使用对象形式)
    import React, { useState } from 'react'
    import Son from './pages/Son'
    // 1. 引入Context
    import Context from './utils/context'
    
    export default function App() {
      const [msg, setMsg] = useState('根组件传递的数据')
    
      return (
        <>
          {/* 2. 使用Provider包裹上层组件提供数据 */}
          <Context.Provider value={msg}>
            {/* 根组件 */}
            <div>
              <h2>App</h2>
              <Son></Son>
            </div>
          </Context.Provider>
        </>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 数据消费组件,用useContext这个hook,或者通过Consumer标签接收显示数据

    使用useContext这个hook

    import React, { useContext } from 'react'
    import Sun from './Sun'
    import Context from '../utils/context'
    
    export default function Son() {
      let val = useContext(Context)
    
      return (
        <div>
          <h3>Son</h3>
          <p>从根组件得到的数据 --- {val}</p>
          <Sun></Sun>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    通过Consumer标签

    import React from 'react'
    import Context from '../utils/context'
    
    export default function Sun() {
      return (
        <div>
          <h5>Sun</h5>
          <div>
            从根组件得到的数据:
            <Context.Consumer>{(value) => <span>{value}</span>}</Context.Consumer>
          </div>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    组件进阶

    children属性
    • 表示该组件的子节点,只要组件内部有子节点,props中就有该属性

    • children属性,类似于插槽。直接写在标签中的内容会填充到children属性上面

    • children可以是普通文本普通标签元素函数 / 对象JSX

    • 如果并列的传入多个,propschildren属性会变成一个数组(就可以直接进行遍历)

    父组件

    import React from 'react'
    import Son from './pages/Son'
    
    export default function App() {
      return (
        <div>
          <h2>App</h2>
          <Son>
            普通文本:666
            <div>普通标签元素</div>
            {/* 函数 */}
            {function fn() {
              console.log('函数打印')
            }}
            {/* JSX结构 */}
            {
              <div>
                <p>{'这是一个普通的jsx结构'}</p>
              </div>
            }
          </Son>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    子组件

    import React from 'react'
    
    export default function Son(props) {
      console.log(props)
      return (
        <div>
          <h3>Son</h3>
          <p>{props.children[0]}</p>
          {props.children[1]}
          {props.children[2]()}
          {props.children[3]}
    	  <hr />
          {props.children.map((item) => {
            return item
          })}
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    props校验
    1. 下载 prop-types 插件 ,并导入prop-types包 yarn add prop-types
    2. 使用 组件名.propsTypes = { } 来给组件的props中的数据添加校验规则
    3. 校验规则通过PropTypes对象来指定

    检验基本语法

    组件名.prototype = {
    属性名 : PropTypes.XXX,
    }

    • PropTypes是引入的prop-types插件的实例

    设置默认值

    组件名.defaultProps= {
    属性名 : 默认值,
    }

    • 或者在参数上直接给默认值

    父组件

    import React, { useState } from 'react'
    import Son from './pages/Son'
    
    export default function App() {
      const [list] = useState([
        {
          id: 0,
          name: '张三',
        },
        {
          id: 1,
          name: '李四',
        },
      ])
      const [obj] = useState({
        name: '王五',
        age: 24,
      })
    
      return (
        <div>
          <h2>App</h2>
          <Son list={list} score={100} obj={obj}></Son>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    子组件

    import React from 'react'
    import PropTypes from 'prop-types'
    
    export default function Son(props) {
      let { list, score, obj } = props
    
      return (
        <div>
          <h3>Son</h3>
          <ul>
            {list.map((item) => (
              <li key={item.id}>{item.name}</li>
            ))}
          </ul>
          <p>成绩是:{score}</p>
          <p>姓名:{obj.name}</p>
          <p>年龄:{obj.age}</p>
        </div>
      )
    }
    
    // 对传过来的值进行校验
    Son.propTypes = {
      list: PropTypes.array.isRequired,
      // 也可以自定义校验规则  peops是所有接收过来的数据,propsName是字段名,componentName组件名
      score: function (props, propsName, componentName) {
        if (props[propsName] < 60) {
          return new Error('成绩不合格')
        }
      },
      obj: PropTypes.shape({
        name: PropTypes.string,
        age: PropTypes.number,
      }),
    }
    
    // 设置默认值
    Son.defaultProps = {
      list: [],
      score: 100,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    常见规则

    • 常见类型: array bool func number object string
    • React元素类型(JSX): element
    • 是否必填: isRequired
    • 特定结构的对象: shape({ }) 也就是指定对象里面字段的规则,可以指定一个,也可以指定多个
    // 特定结构的对象
    obj: PropTypes.shape({
      name: PropTypes.string,
      age: PropTypes.number,
    }),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 也可以自定义校验规则(见上面的例子)

    Hook

    useState
    • useState(初始值)返回值是一个数组(里面有两项)
    • [数据,修改数据的方法] 是对 useState进行结构。把里面的两项分别结构出来

    格式:

    let/const [ 数据 ,修改数据的方法 ] = useState(默认值)

    eg:

    import React from 'react'
    import { useState } from 'react'
    
    export default function App() {
      const [count, setCount] = useState(0)
      console.log(useState(0)) // (2) [0, ƒ]
      const add = (num) => {
        let newCount = count + num
        setCount(newCount)
      }
    
      return (
        <div>
          <h2>App --- {count}</h2>
          <button onClick={() => add(1)}>+1</button>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    函数做为参数

    • useState 中也可以传入一个函数做为参数(初始值可能需要经过一些计算而得)
    • 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过计算才能获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用

    格式:

    const [name, setName] = useState(()=>{

    // 编写计算逻辑 return ‘计算之后的初始值’

    })

    eg:

    父组件

    import React from 'react'
    import { useState } from 'react'
    import Son from './pages/Son'
    
    export default function App() {
      const [count, setCount] = useState(0)
      const countEdit = (num) => {
        setCount(num)
      }
    
      return (
        <div>
          <h2>App</h2>
          <button onClick={() => countEdit(10)}>10</button>
          <button onClick={() => countEdit(20)}>20</button>
          <Son count={count}></Son>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    子组件

    import React from 'react'
    import { useEffect } from 'react'
    import { useState } from 'react'
    
    export default function Son(props) {
      const [c, setc] = useState(() => props.count)
      useEffect(() => {
        setc(props.count)
      }, [props])
    
      return (
        <div>
          <h3>Son -- {c}</h3>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    useEffect
    • useEffect函数的作用就是为react函数组件提供副作用处理的
    • useEffect都是在组件dom渲染更新完毕之后才执行的

    副作用

    副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)

    常见的副作用

    1. 数据请求 ajax发送
    2. 手动修改dom
    3. localstorage操作

    执行时机

    1.不添加依赖项

    • 组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
      1. 组件初始渲染
      2. 组件更新 (不管是哪个状态引起的更新)
    useEffect(()=>{
        console.log('副作用执行了')
    })
    
    • 1
    • 2
    • 3
    1. 添加空数组
    • 组件只在首次渲染时执行一次
    useEffect(()=>{
    	 console.log('副作用执行了')
    },[])
    
    • 1
    • 2
    • 3
    1. 添加特定依赖项
    • 副作用函数在首次渲染时执行在依赖项发生变化时重新执行
      1. 组件初始渲染
      2. 依赖项发生变化时
    function App() {  
        const [count, setCount] = useState(0)  
        const [name, setName] = useState('zs') 
        
        useEffect(() => {    
            console.log('副作用执行了')  
        }, [count])  
        
        return (    
            <>      
             <button onClick={() => { setCount(count + 1) }}>{count}</button>      
             <button onClick={() => { setName('cp') }}>{name}</button>    
            </>  
        )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意事项

    • useEffect 回调函数中用到的数据(比如,count)就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项就会有bug出现

    清除副作用

    • 在组件被销毁时,如果有些副作用操作需要被清除(比如定时器)

    语法:

    useEffect(() => {

    ​ // 副作用操作…

    ​ return () => {

    ​ // 写清除副作用的代码

    ​ }

    })

    eg: 清除定时器案例

    父组件

    import React from 'react'
    import { useState } from 'react'
    import Son from './pages/Son'
    
    export default function App() {
      const [flag, setFlag] = useState(true)
    
      return (
        <div>
          <h2>App</h2>
          <button onClick={() => setFlag(!flag)}>显示/隐藏组件</button>
          {flag && <Son></Son>}
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    子组件

    import React, { useEffect } from 'react'
    
    export default function Son() {
      // 组件进来的时候触发一个定时器
      useEffect(() => {
        let timer = setInterval(() => {
          console.log('定时器执行了')
        }, 1000)
        // 组件销毁时清除定时器
        return () => {
          // 在return里面的函数里写清除操作
          clearInterval(timer)
        }
      }, [])
      return (
        <div>
          <h3>Son</h3>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    useEffect 发送网络请求

    • 依赖项要是一个空数组,因为依赖项为空数组时只会在页面初始化时触发一次
    import React from 'react'
    import { useEffect } from 'react'
    
    export default function App() {
      const getData = () => {
        fetch('https://cnodejs.org/api/v1/topics')
          .then((response) => response.json())
          .then((data) => console.log(data.data))
      }
      useEffect(() => {
        getData()
      }, [])
    
      return (
        <div>
          <h2>App</h2>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    案例1
    • 求卷去头部距离的hook
    import { useState } from 'react'
    
    export default function useWindowScroll() {
      const [y, sety] = useState('')
      window.addEventListener('scroll', function () {
        sety(this.document.documentElement.scrollTop)
      })
      return [y]
    }
    
    使用
    import React from 'react'
    import useWindowScroll from './hook/useWindowScroll'
    
    export default function App() {
      const [y] = useWindowScroll()
      return (
        <div style={{ height: 1600 }}>
          <h2>App -- {y}</h2>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    案例2
    • 数据改变,会同步到本地
    import { useEffect, useState } from 'react'
    
    export default function useLocalStorage(key, defaultVal) {
      const [val, setVal] = useState(defaultVal)
      // 只要val发生变化,就同步到本地
      useEffect(() => {
        localStorage.setItem(key, val)
      }, [val, key])
      return [val, setVal]
    }
    
    使用
    import React from 'react'
    import useWindowScroll from './hook/useWindowScroll'
    import useLocalStorage from './hook/useLocalStorage'
    
    export default function App() {
      const [y] = useWindowScroll()
      const [val, setVal] = useLocalStorage('val', 0)
      const add = () => {
        setVal(val + 1)
      }
    
      return (
        <div style={{ height: 1600 }}>
          <h2>
            App -- {y} -- {val}
          </h2>
          <button onClick={add}>+1</button>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    useRef
    • 可以获取元素的真实Dom
    import React, { useEffect, useRef } from 'react'
    
    export default function App() {
      const ipt = useRef(null)
      useEffect(() => {
        console.log(ipt.current.value)
      }, [])
    
      return (
        <div>
          <h2>App</h2>
          <input type="text" ref={ipt} />
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    useContext
    • 传输的数据是响应式的,跨组件传输数据用
    • 如果传递的数据,只需要在整个应用初始化的时候传递一次就可以,则可以在index.js文件中提供数据
    • 如果传递的数据需要状态维护,则可以在app.js中提供数据

    使用步骤

    1. 创建一个context的文件
    2. 使用createContext创建Context对象,并导出
    3. 在顶层组件引入,通过Provider提供数据
    4. 在底层组件引入,通过useContext函数获取数据

    举例

    context.js

    import { createContext } from 'react'
    
    const Context = createContext()
    
    export default Context
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上层组件

    import React, { useState } from 'react'
    import Son from './pages/Son'
    // 1. 引入Context
    import Context from './utils/context.js'
    
    export default function App() {
      const [msg] = useState('根组件传递的数据')
    
      return (
        <>
          {/* 2. 使用Provider包裹上层组件提供数据 */}
          <Context.Provider value={msg}>
            {/* 根组件 */}
            <div>
              <h2>App</h2>
              <Son></Son>
            </div>
          </Context.Provider>
        </>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    下层组件

    import React, { useContext } from 'react'
    import Context from '../utils/context.js'
    
    export default function Son() {
      let val = useContext(Context)
    
      return (
        <div>
          <h3>Son</h3>
          <p>从根组件得到的数据 --- {val}</p>
        </div>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    补充

    • document.title 可以获取网页最左上的标题
  • 相关阅读:
    抗肿瘤靶向药物丨小分子化合物 or 单克隆抗体?- MedChemExpress
    微信小程序开发之flex布局及轮播图组件与后台Mock.js交互
    什么是分布式锁?几种分布式锁分别是怎么实现的?
    Windows下hadoop单点部署
    git常用命令
    Qt-FFmpeg开发-打开本地摄像头(6)
    Vue项目实战——【基于 Vue3.x + Vant UI】实现一个多功能记账本(开发导航栏及公共部分)
    简单宿舍管理系统(springboot+vue)
    PMP证书续证流程
    比亚迪、吉利、蔚来等将出席2023第四届中国新能源汽车热管理峰会
  • 原文地址:https://blog.csdn.net/qq_52845451/article/details/128171711