前言
hooks是 16.8 版本之后的新特性,React 团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。 组件的最佳写法应该是函数,而不是类。
React 早就支持函数组件,
但是,这种写法有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类。
React Hooks 的设计目的,就是加强版函数组件,完全不使用”类”,就能写出一个全功能的组件。
Hooks可以让无状态组件实现有状态组件的部分功能,比如设置state,使用钩子函数
useState - 状态
import React, { Fragment, useState } from 'react';
const HooksCompont = () => {
const [count, setCount] = useState(0);
return (
<Fragment>
<h3>hello hooks!! {count}</h3>
<button onClick={() => setCount(count + 1)}>加</button>
</Fragment>
);
};
export default HooksCompont;
useEffect - 副作用
import React, { Fragment, useState, useEffect } from 'react';
const HooksCompont = () => {
const [count, setCount] = useState(0);
const [disable, setDisable] = useState(true);
// useEffect 相当于 conmponentDidMout,conmponentDidUpdate和 conmponentWillUnmount
useEffect(() => {
console.log(count, '页面挂载');
if (count === 2) {
console.log(count, '变化');
}
return () => {
// 这里相当于conmponentWillUnmount (页面离开时)
// 但是如果 第二参数值 发生改变时,触发副作用函数,并且执行里面逻辑
console.log('执行离开');
};
// 当 useEffect 未添加第二个参数时 :页面每次重新渲染(任意点击按钮),都要执行一遍这些副作用函数,显然是不经济的。
// 怎么跳过一些不必要的计算呢?我们需要给 useEffect 传第二个参数即可。
// 用第二个参数来告诉 react只有当 这个参数的值 发生改变时,才执行我们传的副作用函数(第一个参数)。
}, [count]);
useEffect(() => {
return () => {
// 这里相当于conmponentWillUnmount (页面离开时)
console.log('离开');
};
}, []);
return (
<Fragment>
<h3>hello hooks!! {count}</h3>
<button onClick={() => setCount(count + 1)}>加</button>
<button onClick={() => setDisable(!disable)}>{disable ? '启用' : '禁用'}</button>
</Fragment>
);
};
export default HooksCompont;
userReducer - 复杂状态处理
import React, { Fragment, useReducer } from 'react';
const ReducerDemo = () => {
const initialState = { count: 0, name: '鸣人' };
const reducer = (state, action) => {
const { type, payload = 1 } = action;
switch (type) {
case 'increment':
return { ...state, count: state.count + payload };
case 'decrement':
return { ...state, count: state.count - payload };
case 'rename':
return { ...state, name: payload };
default:
throw new Error();
}
};
// 第一位参数是函数, 第二参数为初始值
// useReducer((state, action)=>{}, 0)
// const [state, dispatch] = useReducer(reducer, initialArg);
const [state, dispatch] = useReducer(reducer, initialState);
return (
<Fragment>
<div>
useReducer {state.count} ---- {state.name}
</div>
<button onClick={() => dispatch({ type: 'increment' })}>加</button>
<button onClick={() => dispatch({ type: 'decrement', payload: 2 })}>减</button>
<button onClick={() => dispatch({ type: 'rename', payload: '佐助' })}>changeName</button>
</Fragment>
);
};
export default ReducerDemo;
useContext - 深层值传递
import React, { Fragment, useState, createContext, useContext } from 'react';
// 创建 Context
const theme = createContext('#fff');
const Demo = () => {
return <Child />;
};
const Child = () => {
const father = useContext(theme);
const style = {
backgroundColor: father.bgColor,
};
return (
// 消费者
<Fragment>
{/* 第一种写法 */}
<div style={style}>{father.content}</div>
{/* 第二种 使用 Consumer 属性 */}
<theme.Consumer>{(value) => <div>{value.bgColor}</div>}</theme.Consumer>
</Fragment>
);
};
const HooksContext = () => {
// 深层值传递相当于 生产者 和 消费者模式
const [bgColor, setBgColor] = useState('#fff');
return (
<Fragment>
<input
type="color"
onChange={(e) => {
setBgColor(e.target.value);
}}
/>
{/* 生产者 */}
<theme.Provider value={{ bgColor, content: 'Child' }}>
<Demo />
</theme.Provider>
</Fragment>
);
};
export default HooksContext;
useRef - 引用
import React, { Fragment, useEffect, useRef, forwardRef } from 'react';
const Input = forwardRef((prpos, ref) => {
// 将ref父类的ref作为参数传入函数式组件中,本身props只带有children这个参数,这样可以让子类转发父类的ref,
// 当父类把ref挂在到子组件上时,子组件外部通过forwrardRef包裹,可以直接将父组件创建的ref挂在到子组件的某个dom元素
return <input {...prpos} ref={ref} />;
});
const RefDemo = () => {
// 用于获取元素的原生DOM或者获取自定义组件所暴露出来的ref方法(父组件可以通过ref获取子组件,并调用相对应子组件中的方法)
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
inputRef.current.value = 'hello react';
}, []);
return (
<Fragment>
<Input placeholder="请输入" ref={inputRef} />
</Fragment>
);
};
export default RefDemo;
useMemo
import React, { Fragment, useState, useMemo } from 'react';
const Child = ({ name, children }) => {
const change = () => {
// 要求点击 鸣人 才执行此语句 现点击任何按钮都会执行 导致子组件重新渲染
// 原因 : 父组件任何一个状态发生变化,子组件的代码块都会重新执行一遍
window.console.log('鸣人分身');
return name;
};
// useMemo 是在DOM更新前触发的
const actionChange = useMemo(()=>change(),[name]);
return (
<Fragment>
<div>{actionChange}</div>
<div>{children}</div>
</Fragment>
);
};
const Demo = () => {
const [mingren, setMingren] = useState('鸣人');
const [zuozhu, setZuozhu] = useState('佐助');
return (
<Fragment>
<button
onClick={() => {
setMingren(mingren + 1);
}}
>
鸣人
</button>
<button
onClick={() => {
setZuozhu(zuozhu + 2);
}}
>
佐助
</button>
<Child name={mingren}>{zuozhu}</Child>
</Fragment>
);
};
export default Demo;
轮播图案例
//轮播图数据源
import React from 'react'
import Swiper from '../../components/swiper'
class App extends React.Component {
constructor() {
super();
this.state = {
images: [
{ src: require("../../assets/images/banner1.jpg"), url:"https://baidu.com"},
{ src: require("../../assets/images/banner2.jpg"), url:"https://baidu.com"},
{ src: require("../../assets/images/banner3.jpg"), url:"https://baidu.com"},
{ src: require("../../assets/images/banner3.jpg"), url:"https://baidu.com"},
]
}
}
render() {
return (
<React.Fragment>
<Swiper data={this.state.images} />
</React.Fragment>
)
}
}
export default App;
//轮播图UI
import React from 'react'
import Hoc from './hoc'
import "./style.css"
export default Hoc((props) => {
return (
<div className="banner">
<div className="my-swiper-main" onMouseOver={props.stop} onMouseOut={props.autoPlay}>
{
props.data.length > 0 && props.data.map((item, index) => {
return (
<div className={item.active ? "my-slide show" : "my-slide"} key={index}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
<img src={item.src} alt="" />
</a>
</div>
)
})
}
<div className="pagination">
{
props.data.length > 0 && props.data.map((item, index) => {
return (
<div className={item.active ? "dot active" : "dot"} key={index} onClick={() => { props.change(index) }}></div>
)
})
}
</div>
</div>
</div>
)
})
//轮播图逻辑
import React, { useState, useEffect, useRef, useCallback } from 'react';
export default function Hoc(WithCompont) {
return function HocCompont(props) {
let [data, setData] = useState([]);
let [isInit, setIsInit] = useState(true)
let [iIndex, setIIndex] = useState(0)
// 创建一个表示,通用容器,专门解决 setInterval
let timer = useRef(null);
function change(index) { //点击切换图片
setIIndex(index)
if (data.length > 0) {
for (let i = 0; i < data.length; i++) {
if (data[i].active) {
data[i].active = false;
break;
}
}
}
data[index].active = true;
setData(data)
}
const autoPlay = useCallback(() => { //自动播放
clearInterval(timer.current)
timer.current = setInterval(() => {
let tmpIndex = iIndex;
if (data.length > 0 && data) {
for (let i = 0; i < data.length; i++) {
if (data[i].active) {
data[i].active = false;
break;
}
}
if (tmpIndex >= data.length - 1) {
tmpIndex = 0;
} else {
tmpIndex++
}
data[tmpIndex].active = true;
setIIndex(tmpIndex)
setData(data)
}
}, (3000))
}, [data, iIndex]) // useState 中用到的值需要放在 第二个参数中
function stop() { //鼠标经过清除定时器
clearInterval(timer.current)
}
useEffect(() => {
if (props.data && props.data.length > 0 && isInit) {
setIsInit(false);
for (let i = 0; i < props.data.length; i++) {
if (i === 0) {
props.data[i].active = true;
} else {
props.data[i].active = false
}
}
setData(props.data); //将默认的空数组等于 props.data
}
autoPlay();
return () => { //页面离开清除定时器 (页面离开时执行)
clearInterval(timer.current)
}
}, [props.data, isInit, autoPlay]);
let newsPros = {
change: change,
data: data,
stop: stop,
autoPlay: autoPlay,
}
return (
// <WithCompont {...props} data={data} change={change} stop={stop}></WithCompont>
<WithCompont {...props} {...newsPros}></WithCompont>
)
}
}