Skip to content

三、函数组件

1.语法

jsx
//...
<script type="text/babel">
    function 组件名() {
        return (
            <h1>你好</h1>
        )
    }

    ReactDOM.render(<组件名 />, document.querySelector('#app'))
</script>

2.useState()

作用

react函数组件中用来定义变量

语法

需要从React.useState(xx)中解构出两个属性

  • 第一个是定义的属性名

  • 第二个是更新函数,当修改属性值的时候调用,一般以setXxx形式定义

  • useState()括号中写属性的默认值

  • useState的更新函数是异步

jsx
const [变量名,更新函数]=useState(初始值)

// 定义基本数据类型
const [num,setNum]=useState(66)

// 定义数组和对象
const [arr,setArr]=useState([1,2,3])
const [obj,setObj]=useState({a:1, b:2})

获取

直接在{ }中写属性名

jsx
return (
    <div>
        {num}
    </div>
);

更新

  • 使用解构出来的第二个属性
  • ()里面写新的值
js
setXxx(新数据)

// 更新基本数据类型
setNum(num+1)

3.属性props

函数组件的props是作为构造函数的形参

jsx
const 组件名=(props)=> { // 属性名是props对象的键
    console.log(props) // {a:'1', b:'2',c:3}
    return JSX
}

<组件名 a="1" b="2" c={3} />

状态提升

props可以用来父传子,react中子传父要用到状态提升的思想

把状态和方法都定义在父方法里,通过props传给子组件使用

jsx
// 子组件
const Child=(props)=>{
    return(
        <div>
            {props.visible?<div>
                 <h1>子组件</h1>
             </div>:null}
        </div>
    )
}

//父组件
const Test=()=>{
    const [visible,setVisible]=React.useState(false)
    const show=()=>{
        setVisible(!visible)
    }

    return (
        <div>
            <button onClick={()=>show()}>show</button>
            <Child show={show} visible={visible}/>
        </div>
    )
}
ReactDOM.render(<Test/>, document.querySelector('#app'))

4.useRef()

作用

函数组件通过useRef()这个hook来获取dom元素

语法

1.定义ref变量

jsx
// 可给初始值
const iptRef=useRef(null)

2.给dom元素添加属性

jsx
<input ref={iptRef}/>

3.使用

获取到的ref只有一个属性currentdom元素就存在这个current

jsx
console.log(iptRef.current)

案例

按钮自动聚焦,点击变颜色

jsx
const Test=()=>{
    const btnRef=React.useRef(null)

    React.useEffect(()=>{
        btnRef.current.focus()
    },[])

    const click=()=>{
        btnRef.current.style.background="red"
    }
    return (
        <div>
            <button ref={btnRef} onClick={()=>click()}>show</button>
        </div>
    )
}
ReactDOM.render(<Test/>, document.querySelector('#app'))

注意

useRef()只能获取dom元素,但不能获取组件实例

若要使用子组件的数据和方法,需要在定义子组件时用forwardRef包裹,并使用useImperativeHandle将数据或方法暴露出去

jsx
const 子组件=forwardRef((props,ref)=>{
    
    React.useImperativeHandle(ref,()=>({
        健:值,
        // ...
    }))

    return (
        // ...
    )
})

案例

jsx
// 子组件
const Child=React.forwardRef((props,ref)=>{
    const [num,setNum]=React.useState(6)
    const add=()=>{
        setNum(num+1)
    }

    React.useImperativeHandle(ref,()=>({
        add,
        num
    }))

    return (
        <h1>{num}</h1>
    )
})

//父组件
const Test=()=>{
    const childRef=React.useRef()
    const click=()=>{
        childRef.current.add()
    }
    return (
        <div>
            <button onClick={()=>click()}>num+1</button>
            <Child ref={childRef}/>
        </div>
    )
}
ReactDOM.render(<Test/>, document.querySelector('#app'))

5.useEffect()

作用

react函数组件里用作生命周期

语法

1.仅组件挂载渲染一次相当于componentDidMount

js
useEffect(() => {
	// ...
}, [])

2.首次渲染一次,每当数据更新也会渲染,相当于componentDidUpdate

  • 首次也会渲染,componentDidUpdate首次不会
jsx
useEffect(() => {
	// ...
})

3.销毁前触发,相当于componentWillUnmount

jsx
useEffect(() => {       // componentDidMount
		// ...
	return () => {		// componentWillUnmount
		// ...
	}
}, [])

4.监视属性变化,相当于vue的watch

js
useEffect(() => {
	// ...
}, [state变量名1,state变量名2,.......])

6.useMemo()

​ 当一个父组件中调用了一个子组件的时候,父组件的 state 发生变化,会导致父组件更新,而子组件虽然没有发生改变,但也会进行更新。

​ 简单的理解下,当一个页面内容非常复杂,模块非常多的时候,函数式组件会从头更新到尾,只要一处改变,所有的模块都会进行刷新,这种情况显然是没有必要的。

作用

  • 用来缓存计算属性的,相当于vue里的computed
  • 为了防止父组件更新时,子组件数据发生不必要的渲染
  • useMemo返回函数运行的结果

语法

useMemo有两个参数

  • 第一个是回调函数,必选
  • 第二个是数组,可选
    • 如果不写:首次触发,以后有数据更新也出发
    • 如果写个[]空数组:仅第一次触发
    • 如果数组里面写state数据:则首次出发,且每当数组里的任何一个数据变化,也会出发
js
const [num,setNum]=useState(6)

// 1.不写 - 则只要有状态改变就触发 	
const res = useMemo(() => {
    // ...
    return num*10
})

// 2.写[] - 则仅第一次触发 
const res = useMemo(() => {
    // ...
    return num*10
},[])

// 3.首次触发,每次num改变也会触发
const res = useMemo(() => {
    // ...
    return num*10
},[num])

案例

jsx
const Test=()=>{
    const [num,setNum]=React.useState(6)
    const [num2,setNum2]=React.useState(66)

    const n=React.useMemo(()=>{
        return num*10
    },[num])

    return (
        <div>
            <button onClick={()=>setNum(num+1)}>num+1</button>
            <h1>num:{num}</h1>
            <h1>计算属性:{n}</h1>
            <hr/>
            <button onClick={()=>setNum2(num2+1)}>num2+1</button>
            <h1>num2:{num2}</h1>
        </div>)
}

ReactDOM.render(<Test/>, document.querySelector('#app'))

7.useCallback()

useCallbackuseMemo极其类似,可以说是一模一样,唯一不同的是useMemo返回的是函数运行的结果,而useCallback返回的是函数

作用

  • 用来避免子组件不必要的渲染
  • 缓存函数

案例

jsx
const Test=()=>{
    const [num,setNum]=React.useState(6)
    const [num2,setNum2]=React.useState(66)

    const add=React.useCallback(()=>{
        setNum(num+1)
    },[num2])

    return <div>
        <button onClick={()=>add()}>num+1</button>
        <h1>num:{num}</h1>
        <hr/>
        <button onClick={()=>setNum2(num2+1)}>num2+1</button>
        <h1>num2:{num2}</h1>
    </div>
}
ReactDOM.render(<Test/>, document.querySelector('#app'))

8.useContext()

多级组件通信

1.语法

根组件传数据

jsx
const MyContext = React.createContext();

// 包裹根组件
const [count, setCount] = React.useState(6)

<MyContext.Provider value={ count }>
    <Father />
</MyContext.Provider>

子组件接收数据

jsx
const data = useContext(MyContext);

2.案例

jsx
const MyContext = React.createContext();

function App() {

    const [count, setCount] = React.useState(6)

    return <MyContext.Provider value={ count }>
        <Son1 />
        <Son2 />
        <button onClick={()=> setCount(count+1)}>更新</button>
    </MyContext.Provider>
}

function Son1() {
    const count = React.useContext(MyContext)
    return <div>
        <h1>Son1</h1>
        {count}
    </div>
}

function Son2() {
    const count = React.useContext(MyContext)
    return <div>
        <h1>Son2</h1>
        {count}
    </div>
}

ReactDOM.render(<App />, document.querySelector('#root'))

9.自定义hook

jsx
// 定义hook
const useCus = (val, num) => {
    const [count, setCount] = React.useState(val)

    const add = () => {
        setCount(count + num)
    }
    // 把数据return出去
    return [count, add]
}

// 使用hook
function Test() {
    let [count, add] = useCus(6,2)

    return <div>
        <h1>{count}</h1>
        <button onClick={()=> add(count+1)}>更新</button>
    </div>
}

10.useReducer

状态管理

jsx
function Test() {

    let [store, dispatch] = React.useReducer((store, action) => {
        let tmp = JSON.parse(JSON.stringify(store))
        switch (action.type) {
            case 'ADD':
                tmp.age++
                break;
            case 'DEL':
                tmp.age--
                break;
            default:
                break;
        }
        return tmp
    }, {name:'花花', age: 18})

    return <div>
        <h1>{store.age}</h1>
        <button onClick={()=> dispatch({type:'ADD'})}>更新</button>
    </div>
}

MIT Licensed