函数式组件

名称以大写字母开头
image.png
渲染流程 : 执行了ReactDOM.render(…….之后,发生了什么?

  1. React解析组件标签,找到了MyComponent组件。
  2. 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。

    1. //1.创建函数式组件
    2. function MyComponent(){
    3. console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
    4. return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
    5. }
    6. //2.渲染组件到页面
    7. ReactDOM.render(<MyComponent/>,document.getElementById('test'))

    hooks - V16.8版本以上

    1. 什么是hooks

    Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”
    React体系里组件分为 类组件 和 函数组件
    经过多年的实战,函数组件是一个更加匹配React的设计理念 UI = f(data),也更有利于逻辑拆分与重用的组件表达形式,而先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8开始,Hooks应运而生
    注意点:

  3. 有了hooks之后,为了兼容老版本,class类组件并没有被移除,俩者都可以使用

  4. 有了hooks之后,不能在把函数成为无状态组件了,因为hooks为函数组件提供了状态
  5. hooks只能在函数组件中使用

    2. Hooks解决了什么问题

    Hooks的出现解决了俩个问题 1. 组件的状态逻辑复用 2.class组件自身的问题

  6. 组件的逻辑复用在hooks出现之前,react先后尝试了 mixins混入,HOC高阶组件,render-props等模式但是都有各自的问题,比如mixin的数据来源不清晰,高阶组件的嵌套问题等等

  7. class组件自身的问题class组件就像一个厚重的‘战舰’ 一样,大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this指向问题等等,而我们更多时候需要的是一个轻快灵活的’快艇’

    hooks总结

    image.png

    hooks方法

    useState- 获取state & setState

    (1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
    (2). 语法: const [xxx, setXxx] = React.useState(initValue)
    (3). useState()说明:
    参数: 第一次初始化指定的值在内部作缓存
    返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
    (4). setXxx()2种写法:
    setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
    setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
    newValue需要通过计算才能获得时使用第二种写法

    1. export default function About() {
    2. const [count,setCount] = React.useState(0);
    3. const [list,setList] = React.useState([22]);
    4. function add(){
    5. //普通类型
    6. // setCount(count+1) //方法一
    7. setCount(count => count+1) //方法二
    8. //引用类型
    9. setList([...list,count])
    10. }
    11. return(
    12. <div>
    13. <h2>当前求和为:{count}</h2>
    14. <button onClick={add}>点我+1</button>
    15. </div>
    16. )
    17. }

    useEffect - 生命周期函数

什么是副作用

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

常见的副作用

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

useEffect函数的作用就是为react函数组件提供副作用处理的!

(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

(3). 语法和说明: 
  注意:如果第三个参数不写则只要useState产生数据变化就会执行,相当于监听所有数据
        useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [依赖的状态]) // 如果指定的是[], 回调函数只会在第一次render()后执行,不为空则在该参数变化时才执行

(4). 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
            componentWillUnmount()
(5). useLayoutEffect (同步执行副作用) 使用方式与useEffect一样但调用时机不同, 
useLayoutEffect 和原来 componentDidMount & componentDidUpdate 一致,
在react完成DOM更新后马上同步调用的代码,会阻塞页面渲染。而 useEffect 是会在整个页面渲染完才会调用的代码

useEffect执行时机

export default function About()  {
    const [count,setCount] = React.useState(0);

    React.useEffect(()=>{
      //componentDidMount
            let timer = setInterval(()=>{
                setCount(count => count+1 )
            },1000)

      //componentWillUnmount
      return ()=>{
        clearInterval(timer)
      }
    },[count])//componentDidUpdate

}

Ref Hook - 获取ref

(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样

export default function About()  {
    const myRef = React.useRef()

    function show(){
        console.log(myRef.current.value);
    }

    return(
        <div>
          <input type="text" ref={myRef}/>
                    <button onClick={show}>点我提示数据</button>
        </div>
    )
}

useCallback(记忆函数)

防止因为组件重新渲染,导致方法被重新创建 ,起到缓存作用; 只有第二个参数 变化了,才重新声明一次

//useCallback 不会执行第一个参数函数,而是将它返回给你 所以常用记忆事件函数,
//生成记忆后的事件函数并传递给子组件使用
var handleClick = useCallback((evt)=>{
  console.log(name)
},[name])

<button onClick={()=>handleClick()}>hello</button>

//只有name改变后, 这个函数才会重新声明一次,
//如果传入空数组, 那么就是第一次创建后就被缓存, 如果name后期改变了,拿到的还是老的name。
//如果不传第二个参数,每次都会重新声明一次,拿到的就是最新的name.

useMemo 记忆组件

与 useCallback 唯一的区别是:useCallback 不会执行第一个参数函数,而是将它返回给你,而 useMemo 会执行第一个函数并 且将函数执行结果返回给你。
所以 useCallback 常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而 useMemo 更适合经过函数 计算得到一个确定的值,比如记忆组件。

//useMemo 更适合经过函数 计算得到一个确定的值    
const getCinemaList = useMemo(
      () => cinemaList.filter() //过滤出一个数组交给dom渲染
  , [cinemaList])

return <div>
  {
    getCinemaList.map()
  }
</div>

useContext (Provider)

可以减少组件层级传递数据
类似vue的provide 与 inject

import React, { useState,useEffect,useContext } from 'react'
import axios from 'axios'
import './css/index.css'

const GlobalContext  = React.createContext() //创建context对象

export default function App (){
    const [filmList, setfilmList] = useState([])
    const [info, setinfo] = useState("")

    useEffect(() => {
        axios.get(`/test.json`).then(res=>{
            // console.log(res.data.data.films)
            setfilmList(res.data.data.films)
        })
    }, [])

    return (
        //包裹子组件 传递value
        <GlobalContext.Provider value={{
            call:"打电话",
            sms:"短信",
            info:info,
            changeInfo:(value)=>{
                setinfo(value)
            }
        }}>
            <div>
                {/* {this.state.info} */}
                {
                    filmList.map(item=>
                        <FilmItem key={item.filmId} {...item} ></FilmItem>    
                    )
                }

                //子组件
                <FilmDetail ></FilmDetail>
            </div>
        </GlobalContext.Provider>
    )
}

/*受控组件*/
function FilmItem(props){
    let {name, poster,grade,synopsis}  = props
    //子组件获取value
    const value = useContext(GlobalContext)

    // console.log(context)
    return <div className="filmitem" onClick={()=>{
                //调用value
                value.changeInfo(synopsis)
            }}>
                <img src={poster} alt={name}/>
                <h4>{name}</h4>
                <div>观众评分:{grade}</div>
            </div> 
}



function FilmDetail(){
  //子组件获取value
    const value = useContext(GlobalContext)
    return <div className="filmdetail">
        detail-{value.info}
    </div>
}

useReducer (react自己的redux)

import React,{useReducer} from 'react'
 //处理函数reducer
 const reducer = (prevState,action)=>{
     console.log("reduercer",prevState,action)
     let newstate = {...prevState}
     switch(action.type){
         case "kerwin-minus":
            newstate.count--
            return newstate

         case "kerwin-add":
            newstate.count++
            return newstate

         default:
            return prevState
     }
 }
 // 外部的对象
 const intialState = {
     count:0,
    //  list:[]
 } 

 export default function App() {
   //接收两个参数1.处理函数 2.对象数据 
     const [state, dispatch] = useReducer(reducer,intialState)

     return (
         <div>
             <button onClick={()=>{
               //触发reducer
                 dispatch({
                     type:"kerwin-minus"
                 })
             }}>-</button>
             {state.count}
             <button onClick={()=>{
                //触发reducer
                 dispatch({
                    type:"kerwin-add"
                })
             }}>+</button>
         </div>
     )
 }

useReducer和useContext 结合使用

通过useContext给后代传递useReducerstatedispatch,完成多个组件对同一数据的使用和修改

import React, { useEffect,useContext,useReducer } from 'react'
import axios from 'axios'
import './css/index.css'

const GlobalContext  = React.createContext() //创建context对象


const intialState  ={
    info:"",
    filmList:[]
}

const reducer = (prevState,action)=>{
    var newState = {...prevState}

    switch(action.type){
        case "change-filmlist":
            newState.filmList = action.value
            return newState
        case "change-info":
            newState.info = action.value
            return newState
        default :
            return prevState
    }
}

export default function App (){

    const [state,dispatch]  =useReducer(reducer,intialState)

    useEffect(() => {
        axios.get(`/test.json`).then(res=>{
            // console.log(res.data.data.films)

            dispatch({
                type:"change-filmlist",
                value:res.data.data.films
            })
        })
    }, [])

    return (
        <GlobalContext.Provider value={
            {
                state,
                dispatch
            }
        }>
            <div>
                {/* {this.state.info} */}
                {
                    state.filmList.map(item=>
                        <FilmItem key={item.filmId} {...item} ></FilmItem>    
                    )
                }


                <FilmDetail ></FilmDetail>
            </div>
        </GlobalContext.Provider>
    )
}

/*受控组件*/

function FilmItem(props){
    let {name, poster,grade,synopsis}  = props
    const {dispatch} = useContext(GlobalContext)

    // console.log(context)
    return <div className="filmitem" onClick={()=>{
                console.log(synopsis)
                // this.props.onEvent(synopsis)

                // value.info = "2222222"

                // console.log(value)

                dispatch({
                    type:"change-info",
                    value:synopsis
                })
            }}>
                <img src={poster} alt={name}/>
                <h4>{name}</h4>
                <div>观众评分:{grade}</div>
            </div> 
}



function FilmDetail(){
    const {state} = useContext(GlobalContext)
    return <div className="filmdetail">
        detail-{state.info}
    </div>
}

自定义hook-抽离可复用业务组件 - fetch获取数据

注意:

  1. hooks以use开头
  2. hooks是钩子函数 只能在函数组件实体内部调用,不能在函数体内调用,下图会报错 ```jsx import React, { useState,useEffect,useMemo } from ‘react’ import axios from ‘axios’

//自定义hook - 获取电影列表 function useCinemaList(){ const [cinemaList, setcinemaList] = useState([]) useEffect(() => { axios({ url:”https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159“, method:”get”, headers:{ ‘X-Client-Info’: ‘{“a”:”3000”,”ch”:”1002”,”v”:”5.0.4”,”e”:”16395416565231270166529”,”bc”:”110100”}’,

            'X-Host': 'mall.film-ticket.cinema.list'

        }
    }).then(res=>{
        setcinemaList(res.data.data.cinemas)
    })
}, [])

return {
    cinemaList
}

} //自定义hook - 对电影列表数据进行过滤 function useFilter(cinemaList,mytext){ const getCinemaList = useMemo(() => cinemaList.filter(item=>item.name.toUpperCase().includes(mytext.toUpperCase()) || item.address.toUpperCase().includes(mytext.toUpperCase()) ), [cinemaList,mytext])

return {
    getCinemaList
}

} //电影列表组件 export default function Cinema(){ const [mytext, setmytext] = useState(“”)//用于获取搜索电影关键字

const {cinemaList}  = useCinemaList()//获取电影列表

const {getCinemaList} = useFilter(cinemaList,mytext)//获取电影过滤列表

return <div>
        {/* {this.state.mytext} */}
            <input value={mytext} onChange={(evt)=>{
                setmytext(evt.target.value)
            }}/>
            {
                getCinemaList.map(item=>
                    <dl key={item.cinemaId}>
                        <dt>{item.name}</dt>
                        <dd>{item.address}</dd>
                    </dl>    
                )
            }
    </div>

} ```