三、函数组件
1.语法
//...
<script type="text/babel">
function 组件名() {
return (
<h1>你好</h1>
)
}
ReactDOM.render(<组件名 />, document.querySelector('#app'))
</script>2.useState()
作用
react函数组件中用来定义变量
语法
需要从React.useState(xx)中解构出两个属性
第一个是定义的属性名
第二个是更新函数,当修改属性值的时候调用,一般以
setXxx形式定义useState()括号中写属性的默认值
useState的更新函数是异步的
const [变量名,更新函数]=useState(初始值)
// 定义基本数据类型
const [num,setNum]=useState(66)
// 定义数组和对象
const [arr,setArr]=useState([1,2,3])
const [obj,setObj]=useState({a:1, b:2})获取
直接在{ }中写属性名
return (
<div>
{num}
</div>
);更新
- 使用解构出来的第二个属性
- ()里面写新的值
setXxx(新数据)
// 更新基本数据类型
setNum(num+1)3.属性props
函数组件的props是作为构造函数的形参
const 组件名=(props)=> { // 属性名是props对象的键
console.log(props) // {a:'1', b:'2',c:3}
return JSX
}
<组件名 a="1" b="2" c={3} />状态提升
props可以用来父传子,react中子传父要用到状态提升的思想
把状态和方法都定义在父方法里,通过props传给子组件使用
// 子组件
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变量
// 可给初始值
const iptRef=useRef(null)2.给dom元素添加属性
<input ref={iptRef}/>3.使用
获取到的ref只有一个属性current,dom元素就存在这个current里
console.log(iptRef.current)案例
按钮自动聚焦,点击变颜色
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将数据或方法暴露出去
const 子组件=forwardRef((props,ref)=>{
React.useImperativeHandle(ref,()=>({
健:值,
// ...
}))
return (
// ...
)
})案例
// 子组件
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
useEffect(() => {
// ...
}, [])2.首次渲染一次,每当数据更新也会渲染,相当于componentDidUpdate
- 首次也会渲染,componentDidUpdate首次不会
useEffect(() => {
// ...
})3.销毁前触发,相当于componentWillUnmount
useEffect(() => { // componentDidMount
// ...
return () => { // componentWillUnmount
// ...
}
}, [])4.监视属性变化,相当于vue的watch
useEffect(() => {
// ...
}, [state变量名1,state变量名2,.......])6.useMemo()
当一个父组件中调用了一个子组件的时候,父组件的 state 发生变化,会导致父组件更新,而子组件虽然没有发生改变,但也会进行更新。
简单的理解下,当一个页面内容非常复杂,模块非常多的时候,函数式组件会从头更新到尾,只要一处改变,所有的模块都会进行刷新,这种情况显然是没有必要的。
作用
- 用来缓存计算属性的,相当于vue里的
computed - 为了防止父组件更新时,子组件数据发生不必要的渲染
- useMemo返回函数运行的结果
语法
useMemo有两个参数
- 第一个是回调函数,必选
- 第二个是数组,可选
- 如果不写:首次触发,以后有数据更新也出发
- 如果写个
[]空数组:仅第一次触发 - 如果数组里面写state数据:则首次出发,且每当数组里的任何一个数据变化,也会出发
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])案例
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()
useCallback与useMemo极其类似,可以说是一模一样,唯一不同的是useMemo返回的是函数运行的结果,而useCallback返回的是函数
作用
- 用来避免子组件不必要的渲染
- 缓存函数
案例
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.语法
根组件传数据
const MyContext = React.createContext();
// 包裹根组件
const [count, setCount] = React.useState(6)
<MyContext.Provider value={ count }>
<Father />
</MyContext.Provider>子组件接收数据
const data = useContext(MyContext);2.案例
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
// 定义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
状态管理
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>
}