React Native学习之组件通讯三种方案

前文购物车案例:Gouwu组件pop之后,Home组件的已选商品数量如何更新?

前文采用的是 Navigator 传递参数的方式,现做一下总结。

//navigator.push()传递参数
goGouWu() {
    const { navigator } = this.props;
    //为什么这里可以取得 props.navigator?请看上文:
    //<Component {...route.params} navigator={navigator} />
    //这里传递了navigator作为props
    const _that = this;
    if (navigator) {
        navigator.push({
            name: 'GouWu',
            component: GouWu,
            params:{
                getCount: function(count) {
                    _that.setState({
                        count: count,
                    });
                    console.log('参数传回');
                },
            },
        })
    }
}

//navigator.pop()回传参数
paySuccess() {
    var _that = this;
    AsyncStorage.clear(function(err){
        if (!err) {
            _that.setState({
                data: [],
                price: 0,
            });

            const { navigator } = _that.props;
            if (_that.props.getCount) {
                _that.props.getCount(0);
            }

            alert("支付成功!")

            if (navigator) {
                navigator.pop();
            }
            //_that.props.navigator.pop();
        }
    });
}

方案1:通过组件生命周期中更新阶段的回调函数

更新阶段主要发生在用户操作之后或父组件有更新的时候,此时会根据用户的操作行为进行相应的页面结构的调整。更新阶段可依次执行的回调函数包括:

  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

pop之后,数据已经改变了,但是前面的组件没有变化,组件的数据不同步,解决方案:监听didfocus事件,focus到当前路由的时候重新加载数据。

navigationContext.addListener('didfocus', callback)

关键代码:

componentWillMount() {
    console.log('List -> componentWillMount');
    let navigator = this.props.navigator;

    let callback = (event) => {
        console.log(
            'List : 事件类型',
            {
                route: JSON.stringify(event.data.route),
                target: event.target,
                type: event.type,
            }
        );
    };

    // Observe focus change events from this component.
    this._listeners = [
        navigator.navigationContext.addListener('willfocus', callback),
        navigator.navigationContext.addListener('didfocus', callback),
    ];
}

componentWillUnmount() {
    console.log('List -> componentWillUnmount');
    this._listeners && this._listeners.forEach(listener => listener.remove());
}

更新获取导航路由回传数据不用放到componentDidMount,直接放到didfocus的回调即可。

方案2:Navigator参数传递

往下一个路由push的时候传递参数(一个回调),在组件pop之前先调用此回调刷新数据。

关键代码:

goGouWu() {
    const { navigator } = this.props;
    //为什么这里可以取得 props.navigator?请看上文:
    //<Component {...route.params} navigator={navigator} />
    //这里传递了navigator作为props
    let _that = this;
    if (navigator) {
        navigator.push({
            name: 'GouWu',
            component: GouWu,
            params: {
                fetchData: function () {
                    console.log('启动fetchData的方法');
                    AsyncStorage.clear(function (err) {
                        if (!err) {
                            _that.setState({
                                count: 0,
                            });
                            alert('购物车已经清空');
                        }
                    });
                }
            }

        })
    }
}

//在点击清空购物车之后,马上pop,同时触发回调
clearStorage() {
    //触发一下回调 让数据同步
    console.log('点击清空购物车');
    if (this.props.fetchData) {
        console.log('回调清空数据');
        this.props.fetchData();
    }

    const { navigator } = this.props;
    if (navigator) {
        navigator.pop();
    }
}

还有一种情况:在点击清空购物车之后,不马上pop,而是通过点击物理back键去触发回调

错误做法:

const top = routers[routers.length - 1];
console.log('栈顶的路由 -> ' + top.component);
if (top.component.props.fetchData) {
    console.log('回调fetchData');
    top.component.props.fetchData();
}

错误原因:route.component是一个class而不是一个object instance,在里面找props是不可能找得到的。

如果一定需要从route上找到instance,需要在renderScene里给render的东西指定ref
类似<Component ref = {r => route.ref = r} />,然后通过route.ref来访问,但注意不应该假设route.ref总是有值
一定要判断下 if(route.ref)

方案3:采用redux/event等方式完成跨组件通讯

社区主流还是redux,但是建议大家抛弃redux,因为太繁琐了,我们马上有更方便的架构,来自nodejs社区的,不是前端的方案。

React Native官方并不提供这个方案,redux也不是官方的,涉及到十几个插件,核心是decorator,都是npm上的插件。主要是一种前端后端的结构和风格一致的思想,Facebook的野心:学习一次,编写任何平台!

React Native支持Web Android iOS Win10 Nodejs跑服务器端

注意:方案1不推荐,推荐方案2或3

源码

方案一完整代码:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    Navigator,
    ScrollView,
    Image,
    AsyncStorage,
    TouchableOpacity,
    View,
    Platform,
    BackAndroid,
    ToastAndroid,
} from 'react-native';

import Dimensions from 'Dimensions';

const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;

const Model = [
    {
        id: '1',
        title: '红心猕猴桃',
        desc: '12个装70-90g/个',
        price: 49,
        url: 'http://note.youdao.com/yws/api/personal/file/62EAF0C6AC8142EA89B11A71BB6C8AEC?method=download&inline=true&shareKey=e6b1c5b1307f752c9e4925056a4facd9',
    },
    {
        id: '2',
        title: '红富士苹果',
        desc: '5kg果径80-85mm',
        price: 109,
        url: 'http://note.youdao.com/yws/api/personal/file/E38B11859CB2496190BF5A559D9E68B5?method=download&inline=true&shareKey=1dabf3633a1051e9f5d2d7470fd91c0b',
    },
    {
        id: '3',
        title: '哈密瓜',
        desc: '2个装1.4kg以上/个',
        price: 42,
        url: 'http://note.youdao.com/yws/api/personal/file/F443648C199346FDA7E48AE88D0D00A2?method=download&inline=true&shareKey=89fdcc577f98ecdddfcf03f575767381',
    },
    {
        id: '4',
        title: '青芒',
        desc: '1kg500g以上/个',
        price: 28,
        url: 'http://note.youdao.com/yws/api/personal/file/8F7B83A47CA94435A2132DC0341AECC3?method=download&inline=true&shareKey=d679e8800b5678f9c673538b93dc0fbc',
    },
    {
        id: '5',
        title: '黑珍珠葡萄',
        desc: '1kg装',
        price: 32,
        url: 'http://note.youdao.com/yws/api/personal/file/AF297F8C75714DE481E87C6F09D97649?method=download&inline=true&shareKey=8f89c3673e8851efb20abccce2214fbf',
    },
    {
        id: '6',
        title: '红心火龙果',
        desc: '2.5kg装250-350g/个',
        price: 59.9,
        url: 'http://note.youdao.com/yws/api/personal/file/B67D0D453865459594C0FDC6CDE95A79?method=download&inline=true&shareKey=3bc5b3b1ee9f13e3a7ebdd251d117796',
    },
];

class RNAPP extends Component {
    render() {
        let defaultName = 'Home';
        let defaultComponent = Home;
        return (
            <Navigator
                initialRoute = {{ name: defaultName, component: defaultComponent }}
                ref = "navigator"
                //配置场景
                configureScene = {
                    (route) => {
                        //这个是页面之间跳转时候的动画,具体有哪些?可以看这个目录下,有源代码的: node_modules/react-native/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
                        return Navigator.SceneConfigs.VerticalDownSwipeJump;
                    }
                }
                renderScene = {
                    (route, navigator) => {
                        let Component = route.component;
                        return <Component {...route.params} navigator={navigator} />
                    }
                }
            />
        );
    }

    componentWillMount() {
        if (Platform.OS === 'android') {
            BackAndroid.addEventListener('hardwareBackPress', this.onBackAndroid);
            //BackAndroid.addEventListener('hardwareBackPress', this.onBackAndroid, true);
        }
    }

    componentWillUnmount() {
        if (Platform.OS === 'android') {
            BackAndroid.removeEventListener('hardwareBackPress', this.onBackAndroid);
        }
    }

    onBackAndroid = () => {
        const navigator = this.refs.navigator;
        const routers = navigator.getCurrentRoutes();
        console.log('当前路由长度:' + routers.length);
        if (routers.length > 1) {
            navigator.pop();
            return true; //接管默认行为
        } else {
            if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
                //最近2秒内按过back键,可以退出应用。
                return false;
            }
            this.lastBackPressed = Date.now();
            ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT);
            return true;
        }
        return false; //默认行为
    };
}

class Home extends Component {
    render() {
        return (
            <List navigator={this.props.navigator} />
        );
    }
}

class Item extends Component {
    static defaultProps = {
        url: '',
        title: '默认标题',
    };

    static propTypes = {
        url: React.PropTypes.string.isRequired,
        title: React.PropTypes.string.isRequired,
    };

    render() {
        return (
            <View style={styles.item}>
                <TouchableOpacity onPress={this.props.press}>
                    <Image
                        resizeMode='stretch'
                        style={styles.img}
                        source={{ uri: this.props.url }}
                        >
                        <Text numberOfLines={1} style={styles.item_text}>{this.props.title}</Text>
                    </Image>
                </TouchableOpacity>
            </View>
        );
    }
}

class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0,
        };
    }

    shouldComponentUpdate() {
        console.log('List -> shouldComponentUpdate');
        return true;
    }

    componentWillUpdate() {
        console.log('List -> componentWillUpdate');
    }

    componentDidUpdate() {
        console.log('List -> componentDidUpdate');
    }

    componentWillMount() {
        console.log('List -> componentWillMount');
        let navigator = this.props.navigator;

        let callback = (event) => {
            console.log(
                'List : 事件类型',
                {
                    route: JSON.stringify(event.data.route),
                    target: event.target,
                    type: event.type,
                }
            );

            if ('Home' === event.data.route.name && 'didfocus' === event.type) {
                let _that = this;
                AsyncStorage.getAllKeys(
                    function (err, keys) {
                        if (err) {
                            //TODO:存储取数据出错
                            //给用户提示错误信息
                            console.log(err);
                        } else {
                            console.log('读取成功的个数:' + keys.toString());
                        }
                        _that.setState({
                            count: keys.length,
                        });
                    }
                );
            }
        };

        // Observe focus change events from this component.
        this._listeners = [
            navigator.navigationContext.addListener('willfocus', callback),
            navigator.navigationContext.addListener('didfocus', callback),
        ];
    }

    componentWillUnmount() {
        console.log('List -> componentWillUnmount');
        this._listeners && this._listeners.forEach(listener => listener.remove());
    }

    render() {
        console.log('List -> render');
        let list = [];
        for (let i in Model) {
            if (i % 2 === 0) {
                //两个等号:不判断类型
                //三个等号:判断类型
                let row = (
                    <View style={styles.row} key={i}>
                        <Item
                            title={Model[i].title}
                            url={Model[i].url}
                            press={this.press.bind(this, Model[i]) }
                            >
                        </Item>
                        <Item
                            title={Model[parseInt(i) + 1].title}
                            url={Model[parseInt(i) + 1].url}
                            press={this.press.bind(this, Model[parseInt(i) + 1]) }
                            >
                        </Item>
                    </View>
                );
                list.push(row);
            }
        }

        let count = this.state.count;
        let str = null;
        if (count) {
            str = ', 共' + count + '件商品';
        }
        console.log('count=' + count);

        return (
            <ScrollView style={{ marginTop: 10 }}>
                {list}
                <Text onPress={this.goGouWu.bind(this) } style={styles.btn}>去结算{str}</Text>
            </ScrollView>
        );
    }

    goGouWu() {
        //alert('点击了去购物车');
        const { navigator } = this.props;
        //为什么这里可以取得 props.navigator?请看上文:
        //<Component {...route.params} navigator={navigator} />
        //这里传递了navigator作为props
        const _that = this;
        if (navigator) {
            navigator.push({
                name: 'GouWu',
                component: GouWu,
            })
        }
    }

    press(data) {
        this.setState({
            count: this.state.count + 1,
        });
        //AsyncStorage异步存储
        AsyncStorage.setItem('SP-' + this.genId() + '-SP', JSON.stringify(data), function (err) {
            if (err) {
                //TODO:存储出错
                alert(err);
            } else {
                //alert('保存成功');
            }
        });
    }

    //生成随机ID:GUID 全局唯一标识符(GUID,Globally Unique Identifier)是一种由算法生成的二进制长度为128位的数字标识符
    //GUID生成的代码来自于Stoyan Stefanov
    genId() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
            return v.toString(16);
        }).toUpperCase();
    }

    componentDidMount() {
        let _that = this;
        AsyncStorage.getAllKeys(
            function (err, keys) {
                if (err) {
                    //TODO:存储取数据出错
                    //给用户提示错误信息
                    console.log(err);
                } else {
                    console.log('读取成功的个数:'+keys.toString());
                }
                _that.setState({
                    count: keys.length,
                });
            }
        );
    }
}

class GouWu extends Component {
    constructor(props) {
        super(props);
        this.state = {
            price: 0,
            data: [],
        };
    }

    render() {
        //第二次render的时候 data不为空
        let data = this.state.data;
        let price = this.state.price;
        let list = [];
        for (let i in data) {
            price += parseFloat(data[i].price);
            list.push(
                <View style={[styles.row, styles.list_item]} key={i}>
                    <Text style={styles.list_item_desc}>
                        {data[i].title}
                        {data[i].desc}
                    </Text>
                    <Text style={styles.list_item_price}>人民币: {data[i].price}</Text>
                </View>
            );
        }

        let str = null;
        if (price) {
            str = ', 共' + price.toFixed(2) + '元';
        }

        return (
            <ScrollView style={{ marginTop: 10 }}>
                {list}
                <Text style={styles.btn} onPress={this.paySuccess.bind(this)} >支付{str}</Text>
                <Text style={styles.clear} onPress={this.clearStorage.bind(this) }>清空购物车</Text>
            </ScrollView>
        );
    }

    componentDidMount() {
        let _that = this;
        AsyncStorage.getAllKeys(
            function (err, keys) {
                if (err) {
                    //TODO 存储数据出错
                    return;
                }
                //keys是字符串数组
                AsyncStorage.multiGet(keys, function (err, result) {
                    //得到的结构是二维数组
                    //result[i][0]表示我们存储的键,result[i][1]表示我们存储的值
                    let arr = [];
                    for (let i in result) {
                        arr.push(JSON.parse(result[i][1]));
                    }

                    _that.setState({
                        data: arr,
                    });
                });
            }
        );
    }

    paySuccess() {
        console.log('Gouwu -> 点击支付按钮');
        var _that = this;
        AsyncStorage.clear(function(err){
            if (!err) {
                _that.setState({
                    data: [],
                    price: 0,
                });

                const { navigator } = _that.props;
                alert("支付成功!")
                if (navigator) {
                    navigator.pop();
                }
                //_that.props.navigator.pop();
            }
        });
    }

    clearStorage() {
        let _that = this;
        AsyncStorage.clear(function (err) {
            if (!err) {
                _that.setState({
                    data: [],
                    price: 0,
                });
                alert('购物车已经清空');
            }
        });
    }
}

const styles = StyleSheet.create({
    list_item: {
        marginLeft: 5,
        marginRight: 5,
        padding: 5,
        borderWidth: 1,
        height: 32,
        borderRadius: 3,
        borderColor: '#ddd',
    },
    list_item_desc: {
        flex: 2,
        fontSize: 15,
    },
    list_item_price: {
        flex: 1,
        fontSize: 15,
        textAlign: 'right',
    },
    clear: {
        marginTop: 10,
        backgroundColor: '#FF7200',
        color: '#fff',
        borderWidth: 1,
        borderColor: '#ddd',
        marginLeft: 10,
        marginRight: 10,
        lineHeight: 24,
        height: 33,
        fontSize: 18,
        textAlign: 'center',
        textAlignVertical: 'center',
    },
    btn: {
        flex: 1,
        backgroundColor: '#FF7200',
        height: 33,
        textAlign: 'center',
        textAlignVertical: 'center',
        color: '#fff',
        marginLeft: 10,
        marginRight: 10,
        lineHeight: 24,
        marginTop: 40,
        fontSize: 18,
    },
    row: {
        flexDirection: 'row',
        marginBottom: 10,
    },
    img: {
        flex: 1,
        backgroundColor: 'transparent',
    },
    item_text: {
        backgroundColor: '#000',
        opacity: 0.7,
        color: '#fff',
        height: 25,
        lineHeight: 18,
        textAlign: 'center',
        marginTop: 94,
    },
    item: {
        flex: 1,
        marginLeft: 5,
        borderWidth: 1,
        borderColor: '#ddd',
        marginRight: 5,
        height: 120,
    },
    list: {
        justifyContent: 'flex-start',
        flexDirection: 'row',
        flexWrap: 'wrap'
    },
    container: {
        flex: 1,
    },
    listView: {
        paddingTop: 20,
        backgroundColor: '#F5FCFF',
    },
    thumbnail: {
        width: 80,
        height: 80,
        borderRadius: 16,
    },
    //使rightContainer在父容器中占据Image之外剩下的全部空间
    container1: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    title: {
        fontSize: 14,
        marginBottom: 8,
    },
    year: {
        fontSize: 14,
    },
});

AppRegistry.registerComponent('RNAPP', () => RNAPP);

Console输出:

List -> shouldComponentUpdate  //选中第一件商品
List -> componentWillUpdate
List -> render
count=1
List -> componentDidUpdate
List -> shouldComponentUpdate  //选中第二件商品
List -> componentWillUpdate
List -> render
count=2
List -> componentDidUpdate
List : 事件类型 Object {route: "{"name":"GouWu"}", target: NavigationContext, type: "willfocus"}  //即将跳转Gouwu页面
List -> shouldComponentUpdate
List -> componentWillUpdate
List -> render
count=2
List -> componentDidUpdate
List : 事件类型 Object {route: "{"name":"GouWu"}", target: NavigationContext, type: "didfocus"}  //完成跳转Gouwu页面
Gouwu -> 点击支付按钮
List : 事件类型 Object {route: "{"name":"Home"}", target: NavigationContext, type: "willfocus"}  //即将跳回Home页面
List -> shouldComponentUpdate
List -> componentWillUpdate
List -> render
count=2
List -> componentDidUpdate
List : 事件类型 Object {route: "{"name":"Home"}", target: NavigationContext, type: "didfocus"}  //完成跳回Home页面
读取成功的个数:
List -> shouldComponentUpdate
List -> componentWillUpdate
List -> render
count=0
List -> componentDidUpdate

方案二完整代码:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    Navigator,
    ScrollView,
    Image,
    AsyncStorage,
    TouchableOpacity,
    View,
    Platform,
    BackAndroid,
    ToastAndroid,
} from 'react-native';

import Dimensions from 'Dimensions';

const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;

const Model = [
    {
        id: '1',
        title: '红心猕猴桃',
        desc: '12个装70-90g/个',
        price: 49,
        url: 'http://note.youdao.com/yws/api/personal/file/62EAF0C6AC8142EA89B11A71BB6C8AEC?method=download&inline=true&shareKey=e6b1c5b1307f752c9e4925056a4facd9',
    },
    {
        id: '2',
        title: '红富士苹果',
        desc: '5kg果径80-85mm',
        price: 109,
        url: 'http://note.youdao.com/yws/api/personal/file/E38B11859CB2496190BF5A559D9E68B5?method=download&inline=true&shareKey=1dabf3633a1051e9f5d2d7470fd91c0b',
    },
    {
        id: '3',
        title: '哈密瓜',
        desc: '2个装1.4kg以上/个',
        price: 42,
        url: 'http://note.youdao.com/yws/api/personal/file/F443648C199346FDA7E48AE88D0D00A2?method=download&inline=true&shareKey=89fdcc577f98ecdddfcf03f575767381',
    },
    {
        id: '4',
        title: '青芒',
        desc: '1kg500g以上/个',
        price: 28,
        url: 'http://note.youdao.com/yws/api/personal/file/8F7B83A47CA94435A2132DC0341AECC3?method=download&inline=true&shareKey=d679e8800b5678f9c673538b93dc0fbc',
    },
    {
        id: '5',
        title: '黑珍珠葡萄',
        desc: '1kg装',
        price: 32,
        url: 'http://note.youdao.com/yws/api/personal/file/AF297F8C75714DE481E87C6F09D97649?method=download&inline=true&shareKey=8f89c3673e8851efb20abccce2214fbf',
    },
    {
        id: '6',
        title: '红心火龙果',
        desc: '2.5kg装250-350g/个',
        price: 59.9,
        url: 'http://note.youdao.com/yws/api/personal/file/B67D0D453865459594C0FDC6CDE95A79?method=download&inline=true&shareKey=3bc5b3b1ee9f13e3a7ebdd251d117796',
    },
];

class RNAPP extends Component {
    render() {
        let defaultName = 'Home';
        let defaultComponent = Home;
        return (
            <Navigator
                initialRoute = {{ name: defaultName, component: defaultComponent }}
                ref = "navigator"
                //配置场景
                configureScene = {
                    (route) => {
                        //这个是页面之间跳转时候的动画,具体有哪些?可以看这个目录下,有源代码的: node_modules/react-native/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
                        return Navigator.SceneConfigs.VerticalDownSwipeJump;
                    }
                }
                renderScene = {
                    (route, navigator) => {
                        let Component = route.component;
                        return <Component {...route.params} navigator={navigator} />
                    }
                }
            />
        );
    }

    componentWillMount() {
        if (Platform.OS === 'android') {
            BackAndroid.addEventListener('hardwareBackPress', this.onBackAndroid);
            //BackAndroid.addEventListener('hardwareBackPress', this.onBackAndroid, true);
        }
    }

    componentWillUnmount() {
        if (Platform.OS === 'android') {
            BackAndroid.removeEventListener('hardwareBackPress', this.onBackAndroid);
        }
    }

    onBackAndroid = () => {
        const navigator = this.refs.navigator;
        const routers = navigator.getCurrentRoutes();
        console.log('当前路由长度:' + routers.length);
        if (routers.length > 1) {
            navigator.pop();
            return true; //接管默认行为
        } else {
            if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
                //最近2秒内按过back键,可以退出应用。
                return false;
            }
            this.lastBackPressed = Date.now();
            ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT);
            return true;
        }
        return false; //默认行为
    };
}

class Home extends Component {
    render() {
        return (
            <List navigator={this.props.navigator} />
        );
    }
}

class Item extends Component {
    static defaultProps = {
        url: '',
        title: '默认标题',
    };

    static propTypes = {
        url: React.PropTypes.string.isRequired,
        title: React.PropTypes.string.isRequired,
    };

    render() {
        return (
            <View style={styles.item}>
                <TouchableOpacity onPress={this.props.press}>
                    <Image
                        resizeMode='stretch'
                        style={styles.img}
                        source={{ uri: this.props.url }}
                        >
                        <Text numberOfLines={1} style={styles.item_text}>{this.props.title}</Text>
                    </Image>
                </TouchableOpacity>
            </View>
        );
    }
}

class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0,
        };
    }

    render() {
        let list = [];
        for (let i in Model) {
            if (i % 2 === 0) {
                //两个等号:不判断类型
                //三个等号:判断类型
                let row = (
                    <View style={styles.row} key={i}>
                        <Item
                            title={Model[i].title}
                            url={Model[i].url}
                            press={this.press.bind(this, Model[i]) }
                            >
                        </Item>
                        <Item
                            title={Model[parseInt(i) + 1].title}
                            url={Model[parseInt(i) + 1].url}
                            press={this.press.bind(this, Model[parseInt(i) + 1]) }
                            >
                        </Item>
                    </View>
                );
                list.push(row);
            }
        }

        let count = this.state.count;
        let str = null;
        if (count) {
            str = ', 共' + count + '件商品';
        }
        console.log('count=' + count);

        return (
            <ScrollView style={{ marginTop: 10 }}>
                {list}
                <Text onPress={this.goGouWu.bind(this) } style={styles.btn}>去结算{str}</Text>
            </ScrollView>
        );
    }

    goGouWu() {
        //alert('点击了去购物车');
        const { navigator } = this.props;
        //为什么这里可以取得 props.navigator?请看上文:
        //<Component {...route.params} navigator={navigator} />
        //这里传递了navigator作为props
        let _that = this;
        if (navigator) {
            navigator.push({
                name: 'GouWu',
                component: GouWu,
                params: {
                    fetchData: function () {
                        console.log('启动fetchData的方法');
                        AsyncStorage.clear(function (err) {
                            if (!err) {
                                _that.setState({
                                    count: 0,
                                });
                                alert('购物车已经清空');
                            }
                        });
                    }
                }
            })
        }
    }

    press(data) {
        this.setState({
            count: this.state.count + 1,
        });
        //AsyncStorage异步存储
        AsyncStorage.setItem('SP-' + this.genId() + '-SP', JSON.stringify(data), function (err) {
            if (err) {
                //TODO:存储出错
                alert(err);
            } else {
                //alert('保存成功');
            }
        });
    }

    //生成随机ID:GUID 全局唯一标识符(GUID,Globally Unique Identifier)是一种由算法生成的二进制长度为128位的数字标识符
    //GUID生成的代码来自于Stoyan Stefanov
    genId() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
            return v.toString(16);
        }).toUpperCase();
    }

    componentDidMount() {
        let _that = this;
        AsyncStorage.getAllKeys(
            function (err, keys) {
                if (err) {
                    //TODO:存储取数据出错
                    //给用户提示错误信息
                    console.log(err);
                } else {
                    console.log('读取成功的个数:'+keys.toString());
                }
                _that.setState({
                    count: keys.length,
                });
            }
        );
    }
}

class GouWu extends Component {
    constructor(props) {
        super(props);
        this.state = {
            price: 0,
            data: [],
        };
    }

    render() {
        //第二次render的时候 data不为空
        let data = this.state.data;
        let price = this.state.price;
        let list = [];
        for (let i in data) {
            price += parseFloat(data[i].price);
            list.push(
                <View style={[styles.row, styles.list_item]} key={i}>
                    <Text style={styles.list_item_desc}>
                        {data[i].title}
                        {data[i].desc}
                    </Text>
                    <Text style={styles.list_item_price}>人民币: {data[i].price}</Text>
                </View>
            );
        }

        let str = null;
        if (price) {
            str = ', 共' + price.toFixed(2) + '元';
        }

        return (
            <ScrollView style={{ marginTop: 10 }}>
                {list}
                <Text style={styles.btn} onPress={this.paySuccess.bind(this)} >支付{str}</Text>
                <Text style={styles.clear} onPress={this.clearStorage.bind(this) }>清空购物车</Text>
            </ScrollView>
        );
    }

    componentDidMount() {
        let _that = this;
        AsyncStorage.getAllKeys(
            function (err, keys) {
                if (err) {
                    //TODO 存储数据出错
                    return;
                }
                //keys是字符串数组
                AsyncStorage.multiGet(keys, function (err, result) {
                    //得到的结构是二维数组
                    //result[i][0]表示我们存储的键,result[i][1]表示我们存储的值
                    let arr = [];
                    for (let i in result) {
                        arr.push(JSON.parse(result[i][1]));
                    }

                    _that.setState({
                        data: arr,
                    });
                });
            }
        );
    }

    paySuccess() {
        var _that = this;
        AsyncStorage.clear(function(err){
            if (!err) {
                _that.setState({
                    data: [],
                    price: 0,
                });

                //触发一下回调,让数据同步
                console.log('点击清空购物车');
                if (_that.props.fetchData) {
                    console.log('回调清空数据');
                    _that.props.fetchData();
                }

                alert("支付成功!")

                const { navigator } = _that.props;
                if (navigator) {
                    navigator.pop();
                }
                //_that.props.navigator.pop();
            }
        });
    }

    clearStorage() {
        let _that = this;
        AsyncStorage.clear(function (err) {
            if (!err) {
                _that.setState({
                    data: [],
                    price: 0,
                });
                alert('购物车已经清空');
            }
        });
    }
}

const styles = StyleSheet.create({
    list_item: {
        marginLeft: 5,
        marginRight: 5,
        padding: 5,
        borderWidth: 1,
        height: 32,
        borderRadius: 3,
        borderColor: '#ddd',
    },
    list_item_desc: {
        flex: 2,
        fontSize: 15,
    },
    list_item_price: {
        flex: 1,
        fontSize: 15,
        textAlign: 'right',
    },
    clear: {
        marginTop: 10,
        backgroundColor: '#FF7200',
        color: '#fff',
        borderWidth: 1,
        borderColor: '#ddd',
        marginLeft: 10,
        marginRight: 10,
        lineHeight: 24,
        height: 33,
        fontSize: 18,
        textAlign: 'center',
        textAlignVertical: 'center',
    },
    btn: {
        flex: 1,
        backgroundColor: '#FF7200',
        height: 33,
        textAlign: 'center',
        textAlignVertical: 'center',
        color: '#fff',
        marginLeft: 10,
        marginRight: 10,
        lineHeight: 24,
        marginTop: 40,
        fontSize: 18,
    },
    row: {
        flexDirection: 'row',
        marginBottom: 10,
    },
    img: {
        flex: 1,
        backgroundColor: 'transparent',
    },
    item_text: {
        backgroundColor: '#000',
        opacity: 0.7,
        color: '#fff',
        height: 25,
        lineHeight: 18,
        textAlign: 'center',
        marginTop: 94,
    },
    item: {
        flex: 1,
        marginLeft: 5,
        borderWidth: 1,
        borderColor: '#ddd',
        marginRight: 5,
        height: 120,
    },
    list: {
        justifyContent: 'flex-start',
        flexDirection: 'row',
        flexWrap: 'wrap'
    },
    container: {
        flex: 1,
    },
    listView: {
        paddingTop: 20,
        backgroundColor: '#F5FCFF',
    },
    thumbnail: {
        width: 80,
        height: 80,
        borderRadius: 16,
    },
    //使rightContainer在父容器中占据Image之外剩下的全部空间
    container1: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    title: {
        fontSize: 14,
        marginBottom: 8,
    },
    year: {
        fontSize: 14,
    },
});

AppRegistry.registerComponent('RNAPP', () => RNAPP);

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/react-native-learning-for-three-component-communication-schemes/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
React Native学习之组件通讯三种方案
前文购物车案例:Gouwu组件pop之后,Home组件的已选商品数量如何更新? 前文采用的是 Navigator 传递参数的方式,现做一下总结。 //navigator.push()传递参数……
<<上一篇
下一篇>>
文章目录
关闭
目 录