react中函数式组件React Hooks详解
作者:聒噪,
React Hooks
函数式组件
使用hooks理由
- 高阶组件为了复用,导致代码层级复杂
- 生命周期的复杂
- 写成functional组件,无状态组件,因为需要状态,又改成了class,成本高
在16.8版本之前函数式组件无法写状态所以一般只能用class组件成本比较高代码嵌套比较深,并且钩子函数(生命周期相对来说过于复杂)在16.8之后出了函数式组件的状态后,很好的解决了这些弊端。
useState
在之前版本中函数式组件是不支持状态的为了让函数式组件更好的支持状态,可以利用useState
const [name,setName] = useState("Riven")
主要利用这个useState的方法 也是一个数组它有个值一个是Riven,另一个是修改这个状态的方法f()你可以打印一下自己查看console.log(useState(“Riven”)) 如上代码就是将这个状态和这个修改状态的方法给解构出来这样就可以访问到状态,并且进行修改了
// rcc => react component class // rfc => react component function import React,{useState} from 'react' //利用useState让函数式组件支持状态 function App(){ console.log(useState("Riven")); //不支持状态 const [name,setName] = useState("Riven") const [age,setAge] = useState(25) return( <div> app----{name}---{age} <button onClick={()=>{ setName("kerwin") setAge(100) }}>click</button> </div> ) } export default App
以上就是函数式组件的基本写法了你肯定有许多问题?函数式组件的生命周期呢?函数式组件的传值呢通信呢?没有类组件的写法这些应该怎么写呢?
请随我继续往后看
todolist小案例(函数式组件)
首先怎么获取input框中的value值呢?
这里我们有两种方法和类组件差不多
- 就是利用onChage事件
import React,{useState} from 'react' function App(){ const [text, setText] = useState("") return( <div> <input type="text" onChange={(ev)=>{ // console.log(ev.target) setText(ev.target.value) }} /> </div> ) } export default App
- 利用ref
这种方法较为复杂需要利用useRef
import React,{useState,useRef} from 'react' const mytext = useRef(null) <input type="text" onChange={(ev)=>{ // console.log(ev.target) setText(ev.target.value) }} ref={mytext} />
访问的时候可以直接通过mytext.current.value得到value值
import React,{useState,useRef} from 'react' function App(){ const [text, setText] = useState("") const mytext = useRef(null) return( <div> <input type="text" onChange={(ev)=>{ // console.log(ev.target) setText(ev.target.value) }} ref={mytext} /> <button onClick={()=>{ console.log(mytext.current.value) }}>add</button> </div> ) } export default App
完整的todolist
需要注意学习的地方
- input框value值获取
- 怎么让进行添加
- 怎么让添加完成后输入框的value值清空
- 怎么进行删除
import React,{useState,useRef} from 'react' function App(){ const [text, setText] = useState("") const [list,setList] = useState(["111","222","333"]) const mytext = useRef(null) //删除函数只是想写在外面 const handledelclick = (index)=>{ var newlist = [...list] newlist.splice(index,1) setList(newlist) } return( <div> <input type="text" onChange={(ev)=>{ // console.log(ev.target) setText(ev.target.value) }} ref={mytext} value={text} /> <button onClick={()=>{ // console.log(mytext.current.value) //ref setList([...list,text]) //添加数组 setText(" ") }}>add</button> { list.map((item,index)=> <li key={item}> {item} <button onClick={()=>handledelclick(index)}>del</button> </li> ) } </div> ) } export default App
useEffect
在函数式组件中我们没有生命周期useEffect类似生命周期但不是生命周期
我们把数据请求放在函数组件的哪里呢?
- 首先需要知道useEffect接收两个参数,第一个是处理函数,第二是依赖
- 第一个参数相当于一旦创建就会执行。
- 如果第二个参数传入的依赖状态改变也会自动执行
- 如果第二个参数传入的依赖状态为空就相当于值会走一次
- 如果不传任何状态改变都会触发第一个处理函数的执行
- return 出来的这个函数只有销毁的时候才会执行
useEffect(()=>{ console.log("创建"); var id = setInterval(()=>{ console.log(111); },2000) return ()=>{ console.log("销毁") clearInterval(id) } },[])
下来看一个简单的案例
import React,{useState,useEffect} from 'react' const Child = ()=>{ useEffect(()=>{ console.log("创建"); var id = setInterval(()=>{ console.log(111); },2000) return ()=>{ console.log("销毁") clearInterval(id) } },[]) return( <div> Child </div> ) } const App = ()=>{ //useEffect(处理函数,[依赖]) const [text, setText] = useState("1111") const [age, setAge] = useState(25) // useEffect(()=>{ // console.log("创建或者更新的时候执行") // },[text]) //传入空数组相当于cdm //[text] 只有text的改变才会执行一次 //第二参数不传表示任何状态改变都会重新执行 return( <div> App ----{text} <button onClick={()=>{ setText("22222") }}>click-text</button> <br/> {age} <button onClick={()=>{ setAge(100) }}>click-age</button> { age===25? <Child/>: null } </div> ) } export default App
在实际应用中函数式组件会自动传一个参数props 这样我们就可以访问到props里面的属性了
props.match.params.myid props.history.goBack()
import React,{useState,useEffect} from 'react' import { PageHeader} from 'antd'; import axios from 'axios' const Preview = (props)=>{ const [title, settitle] = useState("") const [content, setcontent] = useState("") const [category, setcategory] = useState([]) useEffect(()=>{ axios.get(`http://localhost:8000/articles/${props.match.params.myid}`).then(res=>{ console.log(res.data) let {title,content,category} = res.data settitle(title) setcontent(content) setcategory(category) }) },[props]) return ( <div> <PageHeader className="site-page-header" onBack={() => { // console.log("back",this.props.history) props.history.goBack()//返回上一个页面 }} //返回按钮 title={title} subTitle={category.join("/")} /> <div style={{ padding: "24px" }} dangerouslySetInnerHTML={{ __html:content }}></div> </div> ) } export default Preview
useCallback
防止因为组件重新渲染,导致方法被重新创建 ,起到缓存作用; 只有第二个参数 变化了,才重新声明一次
也就是只需要当需要某个状态改变的时候才让函数执行一次
import React,{useState,useCallback} from 'react' export default function App() { const [text,settext] = useState("1111") const test = useCallback( () => { console.log(text) }, [text], ) test() return ( <div> App-{text} <button onClick={()=>{ settext("22222") }}>click</button> </div> ) }
useContext useReducer
首先这两个都是用来在函数式组件中进行非父子通信的
首先需要创建一个GlobalContext 用来包裹这些生产者的
import React from 'react' const GlobalContext = React.createContext() export default GlobalContext
然后引入
import GlobalContext from './store'
总体代码如下
import React,{useReducer,useContext} from 'react' import GlobalContext from './store' import reducer from './store/reducer' import axios from 'axios' const Child1 = ()=>{ // useContext(GlobalContext) // console.log(useContext(GlobalContext)) let {state,dispatch} = useContext(GlobalContext) return ( <div> child1-----{state.text} <button onClick={()=>{ dispatch({ type:"Change_text", payload:"child11111111111" }) }}>click</button> </div> ) } const Child2 = ()=>{ let {state,dispatch} = useContext(GlobalContext) return ( <div> Child2----{state.text} <button onClick={()=>{ axios.get("http://localhost:8000/rights").then(res=>{ // console.log(res.data) dispatch({ type:"Change_list", payload:res.data }) }) }}>异步数据</button> { state.list.map(item=> <li key={item.id}>{item.title}</li> ) } </div> ) } const App = ()=>{ const [state,dispatch] = useReducer(reducer,{ isShow:true, list:[], text:"我是公共的状态" }) //[公共的状态,改变公共状态的方法] return ( <GlobalContext.Provider value={{ state, dispatch }}> <Child1/> <Child2/> </GlobalContext.Provider> ) } export default App
这里需要注意
const [state,dispatch] = useReducer(reducer,{ isShow:true, list:[], text:"我是公共的状态" }) //[公共的状态,改变公共状态的方法]
这块useReducer(reducer) 其实相当于一个数组里面有两个值一个是公共的状态另外一个改变公共状态的方法
访问的时候可以通过usecontext(GlobalContext) 里面有两个值一个为state,一个为dispatch
const Child1 = ()=>{ // useContext(GlobalContext) // console.log(useContext(GlobalContext)) let {state,dispatch} = useContext(GlobalContext) return ( <div> child1-----{state.text} <button onClick={()=>{ dispatch({ type:"Change_text", payload:"child11111111111" }) }}>click</button> </div> ) }
异步也差不多
const Child2 = ()=>{ let {state,dispatch} = useContext(GlobalContext) return ( <div> Child2----{state.text} <button onClick={()=>{ axios.get("http://localhost:8000/rights").then(res=>{ // console.log(res.data) dispatch({ type:"Change_list", payload:res.data }) }) }}>异步数据</button> { state.list.map(item=> <li key={item.id}>{item.title}</li> ) } </div> ) }
reducer文件
const reducer = (prevstate,action)=>{ let {type,payload} = action switch(type){ case "Change_text": //深复制老状态,返回新状态 // prevstate.text // var newstate = {...prevstate} return { ...prevstate, text:payload } // immutable case "Change_list": return { ...prevstate, list:payload } default: return prevstate } } export default reducer
函数式组件的父子通信问题
这个和类组件差不多,只不过是函数式组件会自动传一个参数props这样就可以直接访问到props里面的属性了
- 父传子(属性)
- 子传父(回调函数)
// 父子通信 ,靠属性 // 父->子 , 属性 // 子->父 , callback( 属性传过去一个回调函数) import React,{useState} from 'react' const Navbar = (props)=>{ console.log(props) return <div> navbar-{props.myname} <button onClick={()=>{ props.onEvent() }}>click</button> </div> } const Sidebar = ()=>{ return <div> Sidebar <ul> <li>111111</li> <li>222222</li> <li>333333</li> </ul> </div> } const Child = (props)=>{ return <div> child --{props.children} </div> } export default function App() { const [show, setshow] = useState(false) return ( <div> <Navbar myname="测试的抽屉" onEvent={()=>{ console.log("父组件中调用") setshow(!show) }}/> { show? <Sidebar/> :null } <Child> <div>child-11111111</div> <div>child-22222222</div> </Child> </div> ) }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。