memo與性能優(yōu)化

避免多層渲染

1. 函數式組件:React.memo()

InfoView.tsx

import React, { useEffect } from 'react';
import {
  StyleSheet,
  View,
  Image,
  Text,
} from 'react-native';

type Props = {
   info: UserInfo,
}

export default React.memo((props: Props) => {
    const {info} = props;

    const styles = darkStyles;

    console.log('render...');
    return (
        <View style={styles.content}>
            <Image style={styles.img} source={{ uri: info.avatar }} />
            <Text style={styles.txt}>{info.name}</Text>
            <View style={styles.infoLayout}>
                <Text style={styles.infoTxt}>
                    {info.desc}
                </Text>
            </View>
        </View>
    );
}, (prevProps: Props, nextProps: Props) => {
    return JSON.stringify(prevProps.info) === JSON.stringify(nextProps.info);
});

const darkStyles = StyleSheet.create({
    content: {
        width: '100%',
        height: '100%',
        backgroundColor: '#353535',
        flexDirection: 'column',
        alignItems: 'center',
        paddingHorizontal: 16,
        paddingTop: 64,
    },
    img: {
        width: 96,
        height: 96,
        borderRadius: 48,
        borderWidth: 4,
        borderColor: '#ffffffE0',
    },
    txt: {
        fontSize: 24,
        color: 'white',
        fontWeight: 'bold',
        marginTop: 32,
    },
    infoLayout: {
        width: '90%',
        padding: 16,
        backgroundColor: '#808080',
        borderRadius: 12,
        marginTop: 24,
    },
    infoTxt: {
        fontSize: 16,
        color: 'white',
    },
});

MemoPage.tsx

import { useState } from "react";
import InfoView from "./InfoView"
import { Button, View } from "react-native";

export default () => {
    const [info, setInfo] = useState<UserInfo>(
        {
            name: '',
            avatar: '',
            desc: ''
        }
    );

     const avatarUri = 'https://upload.jianshu.io/users/upload_avatars/19435884/5c30151f-7756-4071-843e-6ee1c755a031.png?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240';
   
    return (
        <View style={{ width: '100%'}}>
            <Button 
                title="按鈕"
                onPress={() => {

                    setInfo({
                        name: '尼古拉斯',
                        avatar: avatarUri,
                        desc: '各位產品經理大家好,我是個人開發(fā)者張三队贱,我學習RN兩年半了蜒车,我喜歡安卓、RN栋烤、Flutter,Thank you!挺狰。'
                    });
                }}
            />
            <InfoView info={info}/> 
        </View>
        
    );
    
}
2. class組件:shouldComponentUpdate()

InfoView2.tsx

import React, { useEffect } from 'react';
import {
  StyleSheet,
  View,
  Image,
  Text,
} from 'react-native';

type Props = {
   info: UserInfo,
}

export default class InfoView2 extends React.Component<Props, any> {
    constructor(props: Props) {
        super(props);
    }

    shouldComponentUpdate(nextProps: Readonly<Props>): boolean {
        return JSON.stringify(nextProps.info) !== JSON.stringify(this.props.info);
    }
    
    render(): React.ReactNode {
        const { info } = this.props;

        const styles = darkStyles;

        console.log('render 222...');
        return (
            <View style={styles.content}>
                <Image style={styles.img} source={{ uri: info.avatar }} />
                <Text style={styles.txt}>{info.name}</Text>
                <View style={styles.infoLayout}>
                    <Text style={styles.infoTxt}>
                        {info.desc}
                    </Text>
                </View>
            </View>
        );
    }
}

const darkStyles = StyleSheet.create({
    content: {
        width: '100%',
        height: '100%',
        backgroundColor: '#353535',
        flexDirection: 'column',
        alignItems: 'center',
        paddingHorizontal: 16,
        paddingTop: 64,
    },
    img: {
        width: 96,
        height: 96,
        borderRadius: 48,
        borderWidth: 4,
        borderColor: '#ffffffE0',
    },
    txt: {
        fontSize: 24,
        color: 'white',
        fontWeight: 'bold',
        marginTop: 32,
    },
    infoLayout: {
        width: '90%',
        padding: 16,
        backgroundColor: '#808080',
        borderRadius: 12,
        marginTop: 24,
    },
    infoTxt: {
        fontSize: 16,
        color: 'white',
    },
});

MemoPage.tsx

import { useState } from "react";
import InfoView from "./InfoView"
import { Button, View } from "react-native";
import InfoView2 from "./InfoView2";

export default () => {
    const [info, setInfo] = useState<UserInfo>(
        {
            name: '',
            avatar: '',
            desc: ''
        }
    );

     const avatarUri = 'https://upload.jianshu.io/users/upload_avatars/19435884/5c30151f-7756-4071-843e-6ee1c755a031.png?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240';
   
    return (
        <View style={{ width: '100%'}}>
            <Button 
                title="按鈕"
                onPress={() => {

                    setInfo({
                        name: '尼古拉斯',
                        avatar: avatarUri,
                        desc: '各位產品經理大家好明郭,我是個人開發(fā)者張三,我學習RN兩年半了丰泊,我喜歡安卓薯定、RN、Flutter瞳购,Thank you!话侄。'
                    });
                }}
            />
            {/* <InfoView info={info}/>  */}
            <InfoView2 info={info}/>
        </View>
        
    );
    
}

避免重復計算、重復創(chuàng)建對象

1. useMemo緩存數據

ConsumeList.tsx:

import { StyleSheet, View, Text, FlatList, Switch } from "react-native"

import { ListData, TypeColors } from '../constants/Data'
import { useState, useMemo } from "react"

export default () => {
    const [data, setData] = useState<any>(ListData);
    const [typeSwitch, setTypeSwitch] = useState<boolean>(true);

    const cacluateTotal = useMemo(() => {
        // let total = 0;
        // data.forEach((item: any) => {
        //     total += item.amount;
        // });
        // return total;

        console.log("重新計算合計...")

        return data.map((item:any) => item.amount)
                .reduce((pre: number, cur: number) => pre + cur)
    }, [data]);

    const renderItem = ({item, index}:any) => {
        const styles = StyleSheet.create({
            itemLayout: {
                width: '100%',
                flexDirection: 'column',
                borderBottomColor: '#e0e0e0',
                borderBottomWidth: 0.5,
                paddingVertical: 10,
                paddingHorizontal: 10
            },
            titleLayout: {
                width: '100%',
                flexDirection: 'row'
            },
            first: {
                flex: 0.4,
            },
            second: {
                flex: 0.3
            },
            last: {
                flex: 0.6
            },
            txt: {
                flex: 1,
                fontSize: 16,
                color: "#666666",
            },
            valueRow: {
                marginTop: 10
            },
            txtValue: {
                color: 'black',
                fontSize: 14,
                flex: 1,
                fontWeight: 'bold',
            },
            typeTxtValue: {
                color: TypeColors[item.type],
                fontSize: 14,
                flex: 1,
                fontWeight: 'bold',
            }
        })

        return(
            <View style={styles.itemLayout}>
                <View style={styles.titleLayout}>
                    <Text style={[styles.first, styles.txt]}>序號</Text>
                    {typeSwitch && <Text style={[styles.second, styles.txt]}>類型</Text>}
                    <Text style={[styles.txt]}>消費名稱</Text>
                    <Text style={[styles.last, styles.txt]}>消費金額</Text>
                </View>

                <View style={[styles.titleLayout, styles.valueRow]}>
                    <Text style={[styles.first, styles.txtValue]}>{item.index}</Text>
                    {typeSwitch && <Text style={[styles.second, styles.typeTxtValue]}>{item.type}</Text>}
                    <Text style={[styles.txtValue]}>{item.name}</Text>
                    <Text style={[styles.last, styles.txtValue]}>{item.amount}</Text>
                </View>
            </View>
        )
    }

    return(
        <View style={styles.root}>
            <View style={styles.titleLayout}>
                <Text style={styles.title}>消費記賬單</Text>
                <Switch
                    style={styles.typeSwitch} 
                    value={typeSwitch}
                    onValueChange={(value) => {
                        setTypeSwitch(value);
                    }}
                />
            </View>
            
            <FlatList
                data={data}
                keyExtractor={(item, index) => `${item.index}-${item.name}`}
                renderItem={renderItem}
            >
            </FlatList>

            <View style={styles.totalLayout}>
                <Text style={styles.totalTxt}>{cacluateTotal}</Text>
                <Text style={styles.totalTxt}>合計:</Text>
            </View>
        </View>
    )
}

const styles = StyleSheet.create({
    root: {
        width: '100%',
        height: '100%',
    },
    titleLayout: {
        width: '100%',
        height: 50,
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'center',
    },
    title: {
        color: 'black',
        fontSize: 18,
        fontWeight: 'bold'
    },
    typeSwitch: {
        position: 'absolute',
        right: 16,
    },
    totalLayout: {
        width: '100%',
        height: 50,
        borderTopColor: '#c0c0c0',
        borderTopWidth: 1,
        flexDirection: 'row-reverse',
        paddingHorizontal: 16,
        paddingVertical: 10
    },
    totalTxt: {
        color: 'black',
        fontWeight: 'bold',
        fontSize: 20
    }
})
2. useMemo緩存ui渲染
//useMemo緩存ui
    const totalAmountView = useMemo(() => {
        console.log("重新渲染合計...")
        const total = data.map((item:any) => item.amount)
                        .reduce((pre: number, cur: number) => pre + cur);
        return(
            <View style={styles.totalLayout}>
                <Text style={styles.totalTxt}>{total}</Text>
                <Text style={styles.totalTxt}>合計:</Text>
            </View>
        );
    }, [data])

···
//使用的地方
{ totalAmountView }
3. useCallback緩存回調函數

我們給item添加點擊事件学赛,如下:

//原始寫法
    const itemPress = (item: any, index: number) => {
        console.log('itemPress...')
    }
<TouchableOpacity
                onPress={() => {
                    itemPress(item, index)
                }}
            >
                <View style={styles.itemLayout}>
                    <View style={styles.titleLayout}>
                        <Text style={[styles.first, styles.txt]}>序號</Text>
                        {typeSwitch && <Text style={[styles.second, styles.txt]}>類型</Text>}
                        <Text style={[styles.txt]}>消費名稱</Text>
                        <Text style={[styles.last, styles.txt]}>消費金額</Text>
                    </View>

                    <View style={[styles.titleLayout, styles.valueRow]}>
                        <Text style={[styles.first, styles.txtValue]}>{item.index}</Text>
                        {typeSwitch && <Text style={[styles.second, styles.typeTxtValue]}>{item.type}</Text>}
                        <Text style={[styles.txtValue]}>{item.name}</Text>
                        <Text style={[styles.last, styles.txtValue]}>{item.amount}</Text>
                    </View>
                </View>
            </TouchableOpacity>

這種寫法onPress需要一個無參數的返回年堆,我們是寫在View內部的,為了避免函數itemPress重復創(chuàng)建盏浇,我們可以使用useCallback來緩存回調函數变丧;但此時我們發(fā)現在onPress中需要的是一個無參數的返回函數,這里也需要做緩存绢掰,那該如何實現呢痒蓬?顯然我們可以使用高階函數來處理,先說一下高階函數如何處理滴劲,再來解說useCallback攻晒。

//高階函數寫法,函數返回一個無參數的函數
    const itemPress = (item: any, index: number) => () => {
        console.log('itemPress...')
    }
onPress={itemPress(item, index)}

現在我們使用useCallback緩存回調函數:

//useCallback避免重復創(chuàng)建函數對象班挖,這里避免了兩層鲁捏,一層是itemPress這個函數,另一層是無參數的這個返回函數聪姿,即onPress需要的
    const itemPress = useCallback((item: any, index: number) => () => {
        console.log('itemPress...')
    }, [])

這里useCallback緩存了兩層碴萧,一層是itemPress這個回調函數,另一層是onPress需要的無參回調函數末购。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末破喻,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子盟榴,更是在濱河造成了極大的恐慌曹质,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異羽德,居然都是意外死亡几莽,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門宅静,熙熙樓的掌柜王于貴愁眉苦臉地迎上來章蚣,“玉大人,你說我怎么就攤上這事姨夹∠舜梗” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵磷账,是天一觀的道長峭沦。 經常有香客問我,道長逃糟,這世上最難降的妖魔是什么吼鱼? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮绰咽,結果婚禮上菇肃,老公的妹妹穿的比我還像新娘。我一直安慰自己剃诅,他們只是感情好巷送,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著矛辕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪付魔。 梳的紋絲不亂的頭發(fā)上聊品,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音几苍,去河邊找鬼翻屈。 笑死,一個胖子當著我的面吹牛妻坝,可吹牛的內容都是我干的伸眶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼刽宪,長吁一口氣:“原來是場噩夢啊……” “哼厘贼!你這毒婦竟也來了?” 一聲冷哼從身側響起圣拄,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤嘴秸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體岳掐,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡凭疮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了串述。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片执解。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖纲酗,靈堂內的尸體忽然破棺而出衰腌,到底是詐尸還是另有隱情,我是刑警寧澤耕姊,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布桶唐,位于F島的核電站,受9級特大地震影響茉兰,放射性物質發(fā)生泄漏尤泽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一规脸、第九天 我趴在偏房一處隱蔽的房頂上張望坯约。 院中可真熱鬧,春花似錦莫鸭、人聲如沸闹丐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卿拴。三九已至,卻和暖如春梨与,著一層夾襖步出監(jiān)牢的瞬間堕花,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工粥鞋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缘挽,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓呻粹,卻偏偏與公主長得像壕曼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子等浊,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容