使用ReactNative(TypeScript)开发实践记录

1,331 阅读4分钟

1.定义类的协议一般是这样的结构

interface IProps {
    propA: any,
    propB: any
}

interface IState {
    stateA: any,
    stateB: any
}

export default class ClaseName extends Component<IProps, IState> {}

ts语法中,这样定义之后,就可以在类中通过this.props.propA以及this.state.stateA来引用值,并且会有代码提示

如果你的类中不需要用到props,只用到state的话,写成export default class ClaseName extends Component<IState>这样的话,在类中调用this.state.stateA,在使用ESLint时,会有报错提示,换成export default class ClaseName extends Component<{},IState>即可。查看Component定义:interface Component<P = {}, S = {}, SS = any>,可发现有P S SS三种协议,虽然不知道原因,但可以猜测P代表的是PropsS代表的是State,然后自己定义的IProps IState分别对应的P S,所以只有State时不定义Props会导致类不认识IState里定义的参数,导致报错。

2.动态获取屏幕宽高,状态栏(statusBar)高度

import { NativeModules, Platform, StatusBar, Dimensions } from 'react-native';

// 获取状态栏高度
let oStatusHeight = 0;
function getStatusHeight(callback?: (height: number) => void) {
    if (oStatusHeight) {
        callback && callback(oStatusHeight);
        return;
    }
    if (Platform.OS === 'ios') {
        const { StatusBarManager } = NativeModules;
        // iOS Only
        if (StatusBarManager && StatusBarManager.getHeight) {
            StatusBarManager.getHeight((statusBar: { height: number }) => {
                const { height } = statusBar;
                oStatusHeight = height;
                callback && callback(oStatusHeight);
            });
        }
    } else {
        const { currentHeight } = StatusBar;
        oStatusHeight = currentHeight || 0;
        callback && callback(oStatusHeight);
    }
}
// 在这里执行一次,是因为获取状态栏高度为异步获取,所以导致不能及时获取到高度,提前调用一下可以在使用之前就已经给`oStatusHeight`赋好值,就不会出现获取失败的情况了
getStatusHeight();

// Dimensions.get('window')获取的是手机中代码可控的区域,
// Dimensions.get('screen')获取的是真正的屏幕尺寸,包括一些代码不可控制的区域(例如安卓手机的状态栏,还有底部的操作栏)
const screen = Dimensions.get('window');
const screenWidth = screen.width;
const screenHeight = screen.height;

3.货币格式化,更多的货币格式化可以研究Intl工具,很强大

// 格式化货币,传入的货币以分为单位,eg: formatMoney(1) = ¥0.01, formatMoney(13644) = ¥136.44, formatMoney(122244) = ¥1,222.44
formatMoney(amount: number | string): string {
    const amoutNumber = Number(amount);
    if (amoutNumber === 0) {
       return '0';
    }

    const formater = Intl.NumberFormat('zh-Hans-CN', {
        style: 'currency',
        currency: 'CNY',
        useGrouping: true // 数字超过1000会加,分隔
    });
    return formater.format(amoutNumber / 100);
}

4.Component支持多个style样式的方法

Componentstyle可以使用数组赋值,即拥有一个样式的集合。

<View style={[viewStyle.styleOne, viewStyle.styleTwo]} />

const viewStyle = StyleSheet.create({
    styleOne: {
        flex: 1,
        backgroundColor: 'red',
        marginTop: 5,
        marginBottom: 10
    },
    styleTwo: {
        backgroundColor: 'green',
        marginLeft: 20
    }
}

代码中View设置了两个样式viewStyle.styleOneviewStyle.styleTwo,既然是数组,那么就是有序的,viewStyle.styleTwo中的属性会覆盖viewStyle.styleOne中相同的属性,同时添加新的属性

5.使用TouchableOpacity包裹ViewText等来实现点击效果,不使用Button

不使用Button的原因是因为Button无法改变标题字体,颜色之类的,样式不灵活。

6.SectionList的一些使用记录 直接上代码:

// 注意这里赋值给SectionList的sections数据源一定要是这样的结构
const tempArr = [{ data: [ any ] }, { data: [ any ]}];

<SectionList
    style={{ flex: 1 }}
    renderItem={this._renderItem}
    renderSectionHeader={this._renderSectionHeader}
    keyExtractor={(item, _) => item.id}
    ListHeaderComponent={this._renderListHeader}
    ListFooterComponent={this._renderListFooter}
    onScroll={this.onScroll}
    sections={tempArr}
    stickySectionHeadersEnabled={true}
/>

// 注意这里的item,一定要用item才能正确的解析到SectionList中sections对应的数据
_renderItem = ({ item }: { item: LabelDetailContentItem }) => {
    return <LabelDetailCell itemData={item} />;
};

SectionListrenderSectionHeader在iOS中自带吸顶效果,在安卓中需要设置stickySectionHeadersEnabledtrue才有同样的吸顶效果。

ListHeaderComponentListFooterComponent分别是整个SectionList的头和尾,头可以用来做下拉刷新等功能,而尾可以用来做加载更多等功能。

7.项目中自己写的一个时间戳转化为时间的工具方法

/** 学习笔记:
 *  获取当前时间:now = new Date();
 *  获取当前月份的时候需要+1(获取的月份为0~11)
 *  通过 now.getDate() 才是获取当前日(几月几日的日), now.getDay()获取的是周几
 */
export default class DateTools {
    public static formatTimeStamp(timeStamp: number): string {
        const nowDate = new Date();
        const nowDateTimeStamp = nowDate.getTime();
        console.log(nowDateTimeStamp);
        if (nowDateTimeStamp < timeStamp) {
            return '';
        }

        const tempDate = new Date(timeStamp);
        const tempYear = tempDate.getFullYear();
        const tempMonth = tempDate.getMonth() + 1;
        const tempDay = tempDate.getDate();
        const tempHours = tempDate.getHours();
        const tempMinutes = tempDate.getMinutes();

        const yearString = `${tempYear}`;
        const monthString = tempMonth < 10 ? `0${tempMonth}` : `${tempMonth}`;
        const dayString = tempDay < 10 ? `0${tempDay}` : `${tempDay}`;
        const hoursString = tempHours < 10 ? `0${tempHours}` : `${tempHours}`;
        const minutesString =
            tempMinutes < 10 ? `0${tempMinutes}` : `${tempMinutes}`;

        const yearDif = nowDate.getFullYear() - tempYear;
        const dayDif = DateTools.getDayBetweenDate(nowDateTimeStamp, timeStamp);
        const hourDif = DateTools.getHoursBetweenDate(
            nowDateTimeStamp,
            timeStamp
        );
        const minuteDif = DateTools.getMinutesBetweenDate(
            nowDateTimeStamp,
            timeStamp
        );

        if (yearDif >= 1) {
            return `${yearString}${monthString}${dayString}日`;
        } else if (dayDif > 1) {
            return `${monthString}${dayString}日`;
        } else if (dayDif === 1) {
            return `昨天 ${hoursString}:${minutesString}`;
        } else if (hourDif >= 1) {
            return `${hoursString}:${minutesString}`;
        } else if (minuteDif >= 1) {
            return `${minuteDif}分钟前`;
        } else {
            return '刚刚';
        }
    }

    private static getDayBetweenDate(
        timeStamp: number,
        otherTimeStamp: number
    ): number {
        return Math.floor((timeStamp - otherTimeStamp) / 1000 / 60 / 60 / 24);
    }

    private static getHoursBetweenDate(
        timeStamp: number,
        otherTimeStamp: number
    ): number {
        return Math.floor((timeStamp - otherTimeStamp) / 1000 / 60 / 60);
    }

    private static getMinutesBetweenDate(
        timeStamp: number,
        otherTimeStamp: number
    ): number {
        return Math.floor((timeStamp - otherTimeStamp) / 1000 / 60);
    }
}

8.stringnumber

stringnumber有两种方法: (1)parseInt() parseFloat() (2)Number()

区别:parseInt()、parseFloat()转换第一个无效字符之前的字符串,例如parseInt(1.2.3)1parseFloat(1.2.3)1.2;Number()如果是不能转换的字符串则输出NaN,例如Number(1.2.3)NaN,如果字符串能被转换成number类型,则Number()函数将判断调用parseInt()还是parseFloat()函数转换。