新聞中心
自React 16.8支持hook以來(lái),RN也在0.59版本支持了hook,而且官方給出的組件實(shí)例也分為了class和hook兩個(gè)版本,可以遇見(jiàn)hook是未來(lái)的趨勢(shì)。恰巧最近重構(gòu)RN項(xiàng)目,把最近遇到的問(wèn)題和思考記錄一下。

今天是總結(jié)課。
重構(gòu)
重構(gòu),我想這個(gè)詞可能沒(méi)有多少人愿意做,在業(yè)務(wù)迭代頻繁的今天,重構(gòu)意味著資源的消耗,而且承擔(dān)著未知的風(fēng)險(xiǎn),那么為什么還要重構(gòu)?
主要有以下思考:
- 代碼沉郁,不敢刪除舊代碼,舊有的代碼邏輯不熟,關(guān)聯(lián)關(guān)系不清,隨著業(yè)務(wù)的迭代,代碼越來(lái)越多。
- 模塊化不清晰,文件混亂。
- 規(guī)范不統(tǒng)一,注釋不明確,代碼雜亂。文件查找困難。
- 工程化不完善,請(qǐng)求不統(tǒng)一,如遇到接口參數(shù)變更,需要修改多處。
- 組件化不完善,公共組件不統(tǒng)一。
hook化
hook化一個(gè)很重要的就是class邏輯的復(fù)用,比如:
- // class
- this.setState({
- count:0
- },()=>{
- // 修改數(shù)據(jù)成功之后 處理的邏輯
- })
- // hook
- useEffect(()=>{
- // 監(jiān)控count值的變更 處理邏輯
- },[count])
如果我們需要監(jiān)控的值很多,是否需要寫很多useEffect呢?這里會(huì)用到另一個(gè)概念,細(xì)粒度組件。寫hook寫多了,會(huì)把一些需要處理的業(yè)務(wù)組件都抽離出來(lái),每個(gè)組件只管自己的狀態(tài)。這樣就會(huì)極大減少了父組件的業(yè)務(wù)堆積和state堆積。
組件多了就會(huì)涉及到組件傳值,這里有三種場(chǎng)景:
- context包裹子組件
- memo + context + reducer 組件傳值
- ref + useImperativeHandle + forwardRef 暴露狀態(tài)給父組件
- props 組件傳值
props傳值,通過(guò)標(biāo)簽屬性傳遞。
- // props 通過(guò)標(biāo)簽傳值
- //
- export default CenterMenu(props){
- const { style, list } = props;
- //. ...
- }
context包裹的話,就會(huì)把所有狀態(tài)都放在了父組件,就會(huì)造成context很臃腫。
- value={{
- serviceBill,
- patientInfo,
- batchList,
- navigation,
- ...params
- }}>
- // ...View
- //
- const Footer = () => {
- const data = useContext(PerfectInfoContext);
- // ...
- }
- export default Footer;
用context包裹一個(gè)Reducer,再用memo緩存,我們就可以在其他函數(shù)組件中去觸發(fā)狀態(tài)變更。
- const initState={
- isLoading: true,
- isSignOut: false,
- userToken: null,
- routes
- }
- const reducer=(prevState, action)=>{ // switch}
- const [state, dispatch] = useReducer(reducer, initState);
- const authContextProps=(dispatch)=>{
- return {
- signIn:async()=>{ // dispatch },
- signOut:async()=>{ // dispatch}
- }
- }
- const authContextData = useMemo(authContextProps(dispatch), []);
- return (
- // view
- );
- // 其他函數(shù)組件
- const { signOut } = useContext(AuthContext);
細(xì)粒度的話,ref應(yīng)該是一個(gè)很好的選擇,拿輸入框組件舉例:
- function FancyInput(props, ref) {
- const inputRef = useRef(null);
- // 暴露給父組件使用
- useImperativeHandle(ref, () => ({
- focus: () => {
- inputRef.current.focus();
- }
- // 其他方法也可以或者state
- }));
- return ;
- }
- export default forwardRef(FancyInput);
- // 父組件
- const fancyRef = useRef(null);
- // useEffect、 onPress中使用
- const onPress=()=>{
- fancyRef.current?.focus()
- }
路由
靈活的路由配置也是我們重構(gòu)要考慮的一部分,怎么在RN中實(shí)現(xiàn)vue項(xiàng)目的路由配置呢?這需要借助React Navigation 5.x以上版本的Stack。比較可惜的是4.x的 NavigationEvents組件被移除。
- // 4.x可使用 4.x之后被移除
- onWillFocus={payload => console.log('will focus', payload)}
- onDidFocus={payload => console.log('did focus', payload)}
- onWillBlur={payload => console.log('will blur', payload)}
- onDidBlur={payload => console.log('did blur', payload)}
- />
- {/* Your view code */}
5.x版本有點(diǎn)倉(cāng)促,已不在維護(hù),變更較大,核心代碼分為native、stack等,可以單獨(dú)使用?,F(xiàn)在的版本6.x大部分api都做了變更,不推薦單獨(dú)升級(jí)。
- // 屏幕事件focus、blur、beforeRemove、state
- React.useEffect(() => {
- const unsubscribe = navigation.addListener('focus', () => {
- // do something
- });
- return unsubscribe;
- }, [navigation]);
回到路由配置上,我們可以通過(guò)routes來(lái)控制路由變化:
- // 路由配置
- export const routes = [
- ...roleRouters,
- {
- name: 'Home',
- screen: HomeScreen,
- hidden: false,
- options: {},
- },
- ...personRouters,
- ...ordersRouters,
- ...goodsRouters,
- ...customerRouters,
- ...taskRouters,
- ...otherRouters,
- ];
- {state.userToken == null ? (
- name='Login'
- component={LoginScreen}/>) :
- (state.routes.map((e, i) => {
- if (!e.hidden) {
- return (
- key={i.toString()}
- name={e.name}
- params={e.params}
- component={e.screen}/>
- );
- }})
- )}
- {/* 公共路由 無(wú)論是否登錄都可以訪問(wèn) */}
- {commonRoutes.map((e, i) =>
)}
如果你覺(jué)得import導(dǎo)入太多的話,navigation也提供了支持,你也可以動(dòng)態(tài)導(dǎo)入:
- // 動(dòng)態(tài)導(dǎo)入
- export const routes = [
- {
- name: 'Home',
- getComponent: () => require('@/pages/other/cmsWeb').default,
- options: { header: () => {} },
- },
- ];
- state.routes.map(
- (e, i) =>
; - )
navigation提供了一個(gè)輔助效果,回到頂部:
- import * as React from 'react';
- import { ScrollView } from 'react-native';
- import { useScrollToTop } from '@react-navigation/native';
- function Albums() {
- const ref = React.useRef(null);
- useScrollToTop(ref); // ScrollView或者FlatList
- return
{/* content */} ;- }
工程化配置
除了babel、eslint配置外,就是模塊化的管理,路由模塊化、頁(yè)面模塊化、api模塊化。工具方法、共用組件、公共hook、公共資源、本地常量,以及屏幕適配方案,剩下就是規(guī)范統(tǒng)一,這樣一個(gè)小中項(xiàng)目基本就可以hold住了。
hook之前都停留著概念上,這次的落地能發(fā)現(xiàn)一些問(wèn)題,也能跟貼切與class對(duì)比,目前還是粗淺使用,更加復(fù)雜的場(chǎng)景還待處理。
補(bǔ)充一點(diǎn)就是,在RN中require的本地圖片返回的是一個(gè)id,那么我們有集中處理必要了。
網(wǎng)頁(yè)名稱:讓我一起聊聊Hook使用總結(jié)
網(wǎng)頁(yè)網(wǎng)址:http://fisionsoft.com.cn/article/djgcsdj.html


咨詢
建站咨詢
