赞
踩
首先我们要知道函数组件是无状态组件,是无法操作state的
。
react hooks是v16.8新增的特性,简单概括,使用hooks可以在函数组件里操作state 和react的其他特性(如:生命周期的钩子函数)。
其本质就是个函数。
格式:
1、定义状态: const [状态名,更新状态的函数] = React.useState(初始值|函数); 如: 声明一个新的叫做 “count” 的 state 变量,初始值为0 。 const [count, setCount] = React.useState(0); //useState函数返回的是数组 相当于类组件中的 this.state={ count :0 } const [person, setPerson] = React.useState({name: '张三疯', age: 18,sex:"女"}) const [person, setPerson] = React.useState(() => ({name: '张三疯', age: 18,sex:"女"})) 2、读取值: {count} {person.name} {person.age} 3、修改值: setCount(5); //对于引用类型,不能局部更新(即:不能只改某个属性),所以,需要使用扩展运算符先拷贝以前所有的属性 setPerson({ ...person, //拷贝之前的所有属性 age:person.age+1, name: '张四疯' //这里的name覆盖之前的name })
注意:
首先,函数式组件重新渲染时,会执行函数里的所有代码;
并且,当函数式组件重新渲染时,是不会再次把状态的值恢复成初始值的。
因为,后续组件重新渲染时,会使用最后一次更新的状态值
示例代码:
import React,{useState} from 'react';
function App() {
// 声明一个叫 "count" 的 state 变量
const [count,setCount] = useState(0); //在App组件重新后,useState 返回的第一个值将始终是更新后最新的 count。
return (
<div className="App">
<p>{count}</p>
<input type="button" value="测试" onClick={()=>{setCount(count+1)}} />
</div>
);
}
对应的函数class组件:
class App extends React.Component {
state = {
count:0
}
render = () => (
<div>
<p>{this.state.count}</p>
<input type="button" value="测试"
onClick={()=>this.setState({count:this.state.count+1})} />
</div>
)
}
可以使得你在函数组件中执行一些带有副作用的方法。
每当 React组件更新之后,就会触发 useEffect,在第一次的render 和每次 update 后的render触发,不用再去考虑“初次挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。
可以把 useEffect
Hook 看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合。
我们在函数式组件里,没有 componentDidMount
,componentDidUpdate
和 componentWillUnmount
,用useEffect。即:当数据发生变化后,渲染到组件上,组件渲染完毕后,就会调用useEffect。
格式:
useEffect(回调函数,[依赖]); //在render之后出发useEffect,进一步调用回调函数
import React,{useState,useEffect} from 'react'; function App() { const [count,setCount] = useState(0); //useEffect:相当于 componentDidMount,componentDidUpdate useEffect(()=>{ console.log("userEffect"); document.title = count; }); return ( <div className="App"> <p>{count}</p> <input type="button" value="测试" onClick={()=>{setCount(count+1)}} /> </div> ); }
2、useEffect的条件执行(useEffect的第二个参数)
当useEffect只有一个参数时,会无条件执行,如果此时发送请求就会无休止的执行(请求引起组件更新,从而再次触发effect执行),那么,可以使用useEffect的第二个参数。
第二个参数表示,useEffect是否再次触发,依赖于某个值。当为空数组时,表示不会二次触发(因为,没有任何依赖)。即:componentDidMount时会触发,componentDidUpdate不会触发。
如下代码,由于依赖是空,所以,useEffect只表示componentDidMount。
useEffect( async ()=>{
let data = await getBooks(); //发送请求的代码已经封装
setBooks(data);
},[]);
如下代码,表示componentDidMount,和 count变化后引起的componentDidUpdate。
useEffect( async ()=>{
let data = await getBooks(); //发送请求的代码已经封装
setBooks(data);
},[count]);
useContext能让Context使用起来变得非常简单。不需要再使用Consumer。使用useContext就能拿到context状态树里的值。
const value = useContext(context对象);
useContext函数传入一个 context 对象(React.createContext
的返回值),返回该 context 的当前值----由上层组件中距离当前组件最近的 的 value
prop 决定。
当组件上层最近的 Provider 更新时,该 Hook 会触发重渲染 。
示例:
//context/index.js import {createContext} from "react"; export default createContext({count:0}); //主入口文件 index.js import ReactDOM from 'react-dom'; import App from './App'; import Context from "./context" let count = 10; ReactDOM.render( <Context.Provider value={count}> <App/> </Context.Provider>, document.getElementById('root') ); //孙子组件 App-->User-->UserAdd.js import myContext from "../../../context"; import {useContext} from "react"; export default (props)=>{ let context = useContext(myContext); console.log(context); return ( <div> <h1>我是用户添加页面</h1> <p>count:{context}</p> //此处使用比起consumer是不是简单的多得多得多了呢? </div> ); }
入redux的状态管理,是一个min版的redux,不支持中间件redux-thunk。
示例:
//reducers.js export let initState ={ count:0 } export let reducer = (state=initState,action)=>{ let {type,payload} = action; switch(type){ case "ADD":return { count:state.count+1 } default:return initState; } } //组件里 import {useReducer} from "react"; import {initState,reducer} from "./reducer"; export default ()=>{ let [state,dispatch] = useReducer(reducer,initState); let handleClick = ()=>{ dispatch({ type:"ADD" }); } return ( <div> <h1>userReducer的使用</h1> <p>{state.count}</p> <input type="button" value=" 加一 " onClick={handleClick} /> </div> ); }
写类组件时,
export default class Parent extends React.Component {
tempfn(v){
}
render = () => (
<div>
<Son person={{name:"张三疯"}} onMyClick={(val)=>this.tempFn(val)} />
</div>
)
}
以上代码存在问题,当父组件(Parent)重新渲染引起子组件Son的渲染。那么就会重新定义对象 ,也会重新定义函数,浪费系统性能和内存。
解决办法如下:
export default class Parent extends React.Component { constructor(props){ super(props); this.state={ person:{name:"张三疯"} } this.tempFn = this.tempFn.bind(this); } tempFn(val){ } render = () => ( <div> <Son person={this.state.person} onMyClick={this.tempFn} /> </div> ) }
但是,在函数式组件里,就没办法了,因为,类组件里可以用this存储这个函数,而在函数式组件了,没法存储这个函数。因为,函数式组件,每次重新渲染时,会把函数里的代码全部执行,而类组件只执行render函数里的代码。
所以:
useCallBack来了:
1、useCallback会返回一个函数的memoized(记忆的)值
2、在依赖不变的情况下,多次定义(如:函数)的时候,返回的值是相同的 。
格式:
let 新的函数 = useCallback(曾经的函数, [依赖的值])
示例代码:
import {useState,useCallback} from "react" export default ()=>{ //1、 定义一个状态(基本类型):count const [count,setCount] = useState(0); let changecount = useCallback(() => { setCount(count + 1) }, [count]) //依赖了count,所以,只要count有变化,就会执行 setCount(count + 1) let changecount = useCallback(() => { setCount(count + 1) }, [])//这种写法,没有依赖值,那么不会执行 setCount(count + 1) return ( <div> <input type="button" value="修改count2" onClick={changecount} /> <p>count:{count}</p> </div> ) }
useCallBack通常在将一个组件中的函数,传递给子元素进行回调使用时,使用useCallback对函数进行处理
示例代码:
1、首先,父组件更新时,子组件也会更新
const MySon = (props)=>{ console.log("props.name",props.name) return <input type="button" value={props.name} /> } export default function Myparent() { const [p,setP] = useState(0) console.log("父组件更新了"); return ( <div> <MySon name="btn1" /> <input type="button" value="加一" onClick={e=>setP(p+1)} /> </div> ) }
2、如果父组件更新时,不希望子组件更新,使用memo( React.memo()
是一个高阶函数,它与 React.PureComponent类似, React.memo()
用在函数式组件里,防止无效的更新)
import {useState,memo} from "react"
const MySon = memo((props)=>{
console.log("props.name",props.name)
return <input type="button" value={props.name} />
})
3、如果父组件修改的数据影响了子组件的数据(props),那么子组件肯定要更新的
const MySon = memo((props)=>{ console.log("props.name",props.name) return <input type="button" value={props.name} /> }) export default function Myparent() { const [p,setP] = useState(0); const [name,setName]=useState("hi"); console.log("父组件更新了"); return ( <div> <MySon name={name} /> {/*点击这个按钮,子组件不更新*,因为,p的值并没有在子组件中使用/} <input type="button" value="加一" onClick={e=>setP(p+1)} /> {/*点击这个按钮,子组件会更新,因为,name是传入子组件的值*/} <input type="button" value="修改Name" onClick={e=>setName("haha")} /> </div> ) }
4、在父组件里定义的函数,每次的值(函数体)是不变了。由于,父组件更新时,重新定义函数(new Function()),那么函数的值(地址)会发生变化。所以,引起子组件的更新了。
const MySon = memo((props)=>{ console.log("props.name",props.name) return <input type="button" value={props.name} onClick={props.increament} /> }) export default function Myparent() { const [p,setP] = useState(0); const [name,setName]=useState("hi"); const increament1 = () => { console.log('执行increament1') } console.log("父组件更新了"); return ( <div> <MySon name={name} increament={increament1} /> {/*点击下面按钮时,看上去并没有修改子组件所使用的的数据name和increment。 但是,实际上,increment1的值发生变化了。 因为: 1、父组件更新时,重新定义了函数(即:重新new Function()),即:变量increment1的值(存储的地址)发生变化了 2、对于memo来说,只做了了浅比较,只要变量的值(对于对象来说,在内存中存储的就是地址)发生了变化,那么,就会引起组件的更新。 */} <input type="button" value="加一" onClick={e=>setP(p+1)} /> </div> ) }
5、useCallBack
把increment1函数用useCallBack进行包裹,这样就不会出现无效的刷新(只要count的值不变,函数increament1就不会重新定义,子组件就不会重新刷新)
const increament1 = useCallback(() => {
console.log('执行increament1')
},[count])
最终代码:
import {useState,useCallback,memo} from "react" const MySon = memo((props)=>{ console.log("props.name",props.name) return <input type="button" value={props.name} onClick={props.increament} /> }) export default function Myparent() { const [p,setP] = useState(0); const [name,setName]=useState("hi"); const [count,setCount] = useState(0); //increment1函数是否要重新定义,取决于count的值是否发生变化 const increament1 = useCallback(() => { console.log('执行increament1') },[count]) console.log("父组件更新了"); return ( <div> <MySon name={name} increament={increament1} /> {/*点击下面这个按钮,子组件不会刷新,因为,并没有修改count的值*/} <input type="button" value="p加一" onClick={e=>setP(p+1)} /> {/*点击下面这个按钮,子组件会刷新,因为,increment1函数是否要重新定义,取决于count的值是否发生变化,下面这个按钮修改了count的值*/} <input type="button" value="count加1" onClick={e=>setCount(count+1)} /> </div> ) }
//格式
useMemo(函数,数组);
//意思:
// 当数组中的其中一个元素,发生变化时,就会调用 函数 。
import React,{useState,useMemo} from "react"; import './App.css'; function Person({ name, age}) { console.log("Person函数"); function genName(name) { console.log('genName') return '姓名:'+name; } // let nameStr = genName(name); //没有使用useMemo const nameStr = useMemo(()=>genName(name),[name]) //此处使用 useMemo return ( <> <div>{nameStr}</div> <hr/> <div>年龄:{age}</div> </> ) } function App() { const [name, setName] = useState('张三疯') const [age, setAge] = useState(12) return ( <> <button onClick={() => setName("姓名"+age)}>修改姓名</button> <button onClick={() => setAge(age+1)}>修改年龄</button> <hr/> <Person name={name} age={age}></Person> </> ) } export default App;
useRef
返回一个可变的 ref 对象,其(ref 对象) .current
属性被初始化为传入的参数(initialValue
)。返回的 ref 对象在组件的整个生命周期内保持不变。
import {useRef} from "react";
let refContainer = useRef(initialValue)
<JSX ref={refContainer} ...
refContainer.current.dom操作
实例:
function TextInputWithFocusButton() { //定义了一个ref变量:inputEl const inputEl = useRef(null); const onButtonClick = () => { // `current` 指向已挂载到 DOM 上的文本输入元素 inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
在非路由跳转的组件里,要获取路由上下文对象,除了可以使用高阶组件withRouter外,react-router-dom里还提供了hooks。
useHistory():
useHistory 返回一个 路由上下文上的history 对象
import { useHistory } from "react-router-dom";
export default (props)=>{
let history = useHistory();
console.log("我是用户添加页面:props",props);
return (
<div>
<h1>我是用户添加页面</h1>
<input type="button" value="跳转" onClick={()=>history.push("/")} />
</div>
);
}
useLocation()
useLocation() 返回一个路由上下文的 location 对象。
<Link to={{pathname:"/User",query:{"id":"01002"}}}>用户管理</Link>
import { useHistory,useLocation } from "react-router-dom";
export default ()=>{
let location = useLocation();
let history = useHistory();
console.log("location",location);
return (
<div>
<h1>我是用户添加页面</h1>
<input type="button" value="跳转" onClick={()=>history.push("/")} />
</div>
);
}
useParams()
useParams() 返回当前匹配的路径上的 params
<Link to={{pathname:"/User/01003"}}>用户管理</Link> import { useHistory,useLocation,useParams } from "react-router-dom"; export default ()=>{ let location = useLocation(); let history = useHistory(); let params = useParams(); console.log("location",location); console.log("params",params); return ( <div> <h1>我是用户添加页面</h1> <input type="button" value="跳转" onClick={()=>history.push("/")} /> </div> ); }
useRouteMatch
useRouteMatch 可以有一个参数 path,如果什么都不传,会返回当前 context 上的 match 的值,一定是 true。如果传了 path,会比较这个 path 和当前 location 是否 match。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。