美文网首页
React 17 基础1

React 17 基础1

作者: 欢欣的膜笛 | 来源:发表于2021-01-17 00:02 被阅读0次

简介

  1. 设计理念
    单向数据流、虚拟 DOM、组件化
  2. 组件化编程的思想
    React 以组件的方式去重新思考用户界面的构成,将用户界面上每一个功能相对独立的模块定义成组件,然后将小组件通过组合或嵌套的方式构成大组件,最终完成整体 UI 的构建。

项目启动

npx create-react-app react-project --template typescript

CSS 模组

  1. 直接引入整个css文件

    import './index.css'
    使用:<div className="app" />
    
  2. JSS 模块化引入组件

    npm install typescript-plugin-css-modules --save-dev
    import styles from './index.module.css'
    使用:<div className={styles .app} />
    

state 和 props 的区别

  1. state 是组件对内的接口,props 是组件对外的接口
  2. state 用于组件内部的数据传递,props 用于数组间数据传递
  3. state:
    • state 是私有的,可以认为 state 是组件的“私有属性”,用 setState() 来修改。若直接修改 state,组件不会触发 render 函数,页面不会渲染。
    • 构建函数 constructor 是唯一可以初始化 state 的地方。调用 setState 后,state 不会立刻更新,是异步操作;不要依赖当前的 state 来计算下一个 state。
  4. props(Properties 的缩写):
    • 本质上说,props 是传入函数的参数,是传入组件内部的数据,是从父组件传递到子组件的数据。
    • props 是只读属性

React Event 事件处理机制

onClick
为了少输入代码,同时为了避免 this 造成的困扰,我们在这里使用箭头函数来进行事件处理。

handleClick = () => {
    this.setState({
      count: this.state.count + 1
    }, () => {
      // 数据同步
      console.log('count', this.state.count)
    })
    // 数据不同步
    console.log('click', this.state.count)
    // 说明 setState 是异步更新,同步执行的
}

render() {
    const { count } = this.state

    return (
      <div>
        <button onClick={this.handleClick}>{count}</button>
      </div>
    )
  }

生命周期

生命周期可分为三个阶段:


图片
  1. Mounting:初始化阶段,创建虚拟 DOM,渲染 UI
    constructor:初始化组件 state
    componentDidMount:在组件创建好 dom 元素以后,挂载进页面时调用,一般用来获取网络数据

  2. Updating:更新阶段,更新虚拟 DOM,重新渲染 UI
    componentWillReceiveProps:在组件接收到一个新的 prop(更新后)时被调用,但是会产生副作用,已被废弃,可使用 getDerivedStateFromProps 来进行替代。

    /*
     * getDerivedStateFromProps:将传入的props映射到state上面。这个函数会在每次 re-rendering 之前被调用,这意味着即使你的 props 没有任何变化,而是父 state 发生了变化,导致子组件发生了 re-render ,这个生命周期函数依然会被调用。
     * getDerivedStateFromProps 是一个静态函数,也就是这个函数不能通过 this 访问到 class 的属性,也并不推荐直接访问属性。而是应该通过参数提供的 nextProps 以及 prevState 来进行判断,根据新传入的 props 来映射到 state。
     * 如果 props 传入的内容不需要影响到你的 state,那么就需要返回一个 null,这个返回值是必须的。
     */
    static getDerivedStateFromProps(nextProps, prevState) {
      const {type} = nextProps;
      // 当传入的type发生变化的时候,更新state
      if (type !== prevState.type) {
        return { type }
      }
      // 否则,对于state不进行任何操作
      return null
    }
    

    shouldComponentUpdate:使用 shouldComponentUpdate() 以让 React 知道当前状态或属性的改变是否不影响组件的输出,默认返回 ture ,返回 false 时不会重写 render,该方法并不会在初始化渲染或当使用 forceUpdate() 时被调用

    shouldComponentUpdate(nextProps, nextState) {
      return true
    }
    

    componentDidUpdate:组件更新后调用

  3. Unmounting:删除虚拟 DOM,移除 UI
    componentWillUnmount:组件将要销毁时调用,可用来回收内存、移除监听或清空定时器等

获取网络 API 数据

对 any 的理解:

  1. 资源来源于网络请求,返回的数据类型不受控制
  2. 前端强行定义 API 数据类型,违法前后端分离的原则
  3. 不能为了使用 Type 而放弃 JavaScript 的灵活性
componentDidMount() {
  fetch('https://jsonplaceholder.typicode.com/users')
      .then(res => res.json())
      .then(data => {
        this.setState({
          robotGallery: data
        })
      })
}

React 17 新版本

  1. 虽然没有新功能,但具有战略意义
  2. 替换了底层的代码,向下兼容,同时甩掉了历史包袱
  3. 没有代码断层,给未来留下了足够的升级空间
  4. 逐步升级方案:允许多个 react 版本共存,如先升级路由系统、再升级弹窗,平滑过渡到最新版本
  5. breaking changes:
    事件委托机制改变
    向原生浏览器靠拢
    删除事件池
    useEffect 清理操作改为异步操作
    JSX 不可返回 undefined
    删除部分私有 API
  6. 展望:v17 不是过渡版本,而是承上启下的战略版本,未来将会更加强调函数事组件,支持微前端等

钩子 hooks 简介

  1. 消息处理的一种方法,用来监视指定程序
  2. 函数组件中需要处理副作用,可以用钩子把外部代码“钩”进来
  3. hooks 一律使用 use 前缀命名:useXxx
  4. 常用钩子:useState、useEffect、useContext、useReducer、useLayoutEffect、useDebugValue
  5. hooks 的本质:一类特殊的函数,为你的函数式组件(functional Component)注入特殊的功能
  6. hooks 的目的是为了给函数式组件加上状态

钩子 hooks 的使用

  1. useState:

    // useState 的参数为 count 的初始值,setCount 用于 count 的更新
    const [ count, setCount ] = useState<number>(0)
    const [ robotGallery, setRobotGallery ] = useState<any[]>([])
    
    <button onClick={() => setCount(count + 1)}>count:{count}</button>
    
  2. useEffect、loading、请求异常

    const [ loading, setLoading ] = useState<boolean>(false)
    const [ errMsg, setErrMsg ] = useState<string>('')
    /*
    * 一个组件内可以有多个useEffect
    * 若第二个参数不传,则在每次页面渲染UI后调用
    * 若第二个参数为空数组,则等同于 componentDidMount
    * 若第二个参数为非空数组,数组内参数变化后调用
    */
    useEffect(() => {
      console.log(count)
    }, [count])
    
    useEffect(() => {
      const getRobots = async () => {
        setLoading(true)
        setErrMsg('')
        try {
          const res = await fetch('https://jsonplaceholder.typicode.com/users')
          if (res.status === 200) {
            const data = await res.json()
            setRobotGallery(data)
          } else {
            setErrMsg(res.status + '')
          }
        } catch (error) {
          setErrMsg(error)
        }
        setLoading(false)
      }
    
      getRobots()
    }, [])
    
    { errMsg && errMsg.trim() && <h4>{errMsg.trim()}</h4>}
    {
       loading
       ?
       <h3>正在加载</h3>
       :
       <div className={styles['robot-layer']}>
         {
           robotGallery.map(item => <Robot key={item.id} id={item.id} name={item.name} phone={item.phone} email={item.email} />)
         }
       </div>
    }
    
  3. 父组件向子组件传递数据(3种)
    第一种:props

    // index.tsx
    <App usename="张三" />
    
    // App.tsx
    <h2>{props.usename}</h2>
    

    第二种:appContext.Consumer

    // index.tsx
    const defauleContextValue = { gender: '男' }
    export const appContext = React.createContext(defauleContextValue)
    
    // 孙子组件:Robot.tsx
    import { appContext } from '../index'
    
    const Robot: React.FC<RobotProps> = ({ id, name, phone, email }) => {
      return (
        <appContext.Consumer>
          {
             value => {
                return (
                  <div className={styles['robot-box']}>
                    <img src={`https://robohash.org/${id}`} alt="robot"/>
                    <p>name:{name}</p>
                    <p>phone:{phone}</p>
                    <p>email:{email}</p>
                    <p>gender:{value.gender}</p>
                  </div>
                )
             }
          }
        </appContext.Consumer>
      )
    }
    

    第三种:useContext

    // 孙子组件:Robot.tsx
    import { appContext } from '../index'
    
    const Robot: React.FC<RobotProps> = ({ id, name, phone, email }) => {
        const value = useContext(appContext)
        return (
            <div className={styles['robot-box']}>
                <img src={`https://robohash.org/${id}`} alt="robot"/>
                <p>name:{name}</p>
                <p>phone:{phone}</p>
                <p>email:{email}</p>
                <p>gender:{value.gender}</p>
            </div>
        )
    }
    
  4. 组件化 context provider
    创建 AppState.tsx 文件

    import React, { useState } from 'react';
    
    interface AppStateValue {
        gender: string
        ShoppingCart: {
            items: { id: number, name: string, count: number }[]
        }
    }
    
    const defauleContextValue: AppStateValue = {
        gender: '男',
        ShoppingCart: {
            items: []
        }
    }
    
    export const appContext = React.createContext(defauleContextValue)
    
    export const appSetStateContext = React.createContext<
        React.Dispatch<React.SetStateAction<AppStateValue>> | undefined
    >(undefined)
    
    export const AppStateProvider: React.FC = props => {
        const [ state, setState ] = useState(defauleContextValue)
    
        return (
            <appContext.Provider value={state}>
                <appSetStateContext.Provider value={setState}>
                    {props.children}
                </appSetStateContext.Provider>
            </appContext.Provider>
        )
    }
    

    index.tsx

    import { AppStateProvider } from './AppState'
    ReactDOM.render(
      <React.StrictMode>
        <AppStateProvider>
          <App />
        </AppStateProvider>
      </React.StrictMode>,
      document.getElementById('root')
    );
    

    子组件,变更全局状态

    import { appContext, appSetStateContext } from '../AppState'
    
    const value = useContext(appContext)
    const setState = useContext(appSetStateContext)
    
    const addToCart = () => {
        if (setState) {
            setState(state => {
                const items = JSON.parse(JSON.stringify(state.ShoppingCart.items))
                const itemIndex = items.findIndex(obj => obj.id === id)
                if (itemIndex !== -1) {
                    items[itemIndex].count++
                    return {
                        ...state,
                        ShoppingCart: {
                            items: [...items]
                        }
                    }
                }
                return {
                    ...state,
                    ShoppingCart: {
                        items: [...items, { id, name, count: 1 }]
                    }
                }
            })
        }
    }
    
    <button onClick={addToCart}>加入购物车</button>
    
  5. 高阶组件(HOC:高阶组件并不是组件,而是接受组件作为参数,并且返回组件的函数):withAddToCart
    AddToCart.tsx

    import React, { useContext } from 'react'
    import { appSetStateContext } from '../AppState'
    import { RobotProps } from './Robot'
    
    export const withAddToCart = (ChildComponent: React.ComponentType<RobotProps>) => {
        return props => {
            const setState = useContext(appSetStateContext)
            const addToCart = (id, name) => {
                  if (setState) {
                    setState(state => {
                        const items = JSON.parse(JSON.stringify(state.ShoppingCart.items))
                        const itemIndex = items.findIndex(obj => obj.id === id)
                        if (itemIndex !== -1) {
                            items[itemIndex].count++
                            return {
                                ...state,
                                ShoppingCart: {
                                    items: [...items]
                                }
                            }
                        }
                        return {
                            ...state,
                            ShoppingCart: {
                                items: [...items, { id, name, count: 1 }]
                            }
                        }
                    })
                }
            }
    
            return <ChildComponent {...props} addToCart={addToCart} />
        }
    }
    

    robot.tsx

    import React, { useContext } from 'react'
    import { appContext } from '../AppState'
    import { withAddToCart } from './AddToCart'
    
    export interface RobotProps {
        id: number
        name: string
        email: string
        phone: string
        addToCart: (id, name) => void
    }
    
    const Robot: React.FC<RobotProps> = ({ id, name, phone, email, addToCart }) => {
        const value = useContext(appContext)
    
        return (
            <div>
                <img src={`https://robohash.org/${id}`} alt="robot"/>
                <p>name:{name}</p>
                <p>phone:{phone}</p>
                <p>email:{email}</p>
                <p>gender:{value.gender}</p>
                <p><button onClick={() => addToCart(id, name)}>加入购物车</button></p>
            </div>
        )
    }
    
    export default withAddToCart(Robot)
    
  6. 自定义 hooks:useAddToCart

    export const useAddToCart = () => {
    const setState = useContext(appSetStateContext)
    
    const addToCart = (id, name) => {
        if (setState) {
            setState(state => {
                const items = JSON.parse(JSON.stringify(state.ShoppingCart.items))
                const itemIndex = items.findIndex(obj => obj.id === id)
                if (itemIndex !== -1) {
                    items[itemIndex].count++
                    return {
                        ...state,
                        ShoppingCart: {
                            items: [...items]
                        }
                    }
                }
                return {
                    ...state,
                    ShoppingCart: {
                        items: [...items, { id, name, count: 1 }]
                    }
                }
            })
          }
        }
    
        return addToCart
    }
    

    robot.tsx

    const addToCart = useAddToCart()
    

React 中,Component、Element、Instance 的区别?

  • 一个元素是一个普通对象,他描述一个组件实例和它所要求的的属性,或者一个 dom 节点和它所要求的属性
  • 组件封装了元素树,组件可以是类或者函数
  • 实例是 Component class 中 this 所指向的东西,即组件实例。Functional Component 没有实例

相关文章

网友评论

      本文标题:React 17 基础1

      本文链接:https://www.haomeiwen.com/subject/uounaktx.html