利用DeviceEventEmitter解决标题栏和数据列表联动问题

这里写图片描述

分析

这里写图片描述

页面包含TitleBar和TabNavigator组件,同时TabNavigator包含三个MessageScreen组件,而在MessageScreen中是FlatList组件,其中的Item是自定义的MessageItem组件。所以嵌套层次还是挺多的。而要实现动画中的效果,需要点击TitleBar中的编辑按钮,刷新MessageItem的状态。此处采用DeviceEventEmitter相对简单点。

当然,如果有更好的方法欢迎交流。

源码

MessageWrapperScreen.js

import React from 'react';
import {StyleSheet, Text, View, ToastAndroid, BackHandler, DeviceEventEmitter, PixelRatio} from 'react-native';
import {TabNavigator} from 'react-navigation';
import TitleBar from "../components/TitleBar";
import MessageScreen from "./MessageScreen";
import theme from "../style/theme";
import Constant from "../Constant";
import BottomButton from "../components/BottomButton";

const MessageNavigator = TabNavigator({
  Tab1: {
    screen: MessageScreen,
    navigationOptions: ({navigation}) => ({
      tabBarLabel: '全部消息'
    })
  },
  Tab2: {
    screen: MessageScreen,
    navigationOptions: ({navigation}) => ({
      tabBarLabel: '销售运营通知'
    })
  },
  Tab3: {
    screen: MessageScreen,
    navigationOptions: ({navigation}) => ({
      tabBarLabel: '小秘书通知'
    })
  }
}, {
  lazy: true,
  tabBarOptions: {
    activeTintColor: theme.activeColor,
    inactiveTintColor: theme.inactiveColor,
    upperCaseLabel: false,
    labelStyle: {
      fontSize: 12,
      marginTop: 0,
      marginBottom: 0,
    },
    indicatorStyle: {
      backgroundColor: theme.activeColor
    },
    style: {
      backgroundColor: '#FFF',
      height: 35,
    },
  },
});

/**
 * 站内信Tab外层页面(包含TabNavigator)
 */
export default class MessageWrapperScreen extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      isEditing: false,//是否正在编辑
    }
  }

  render() {
    const navigation = this.props.navigation;
    return (
      <View style={styles.container}>
        <TitleBar
          nav={navigation}
          title="站内信"
          rightText={this.state.isEditing ? "取消" : "编辑"}
          handleRightClick={this.handleEdit}
          handleBackClick={this.handleBackClick}
        />
        <MessageNavigator navigation={navigation}/>

        {this.state.isEditing
          ? (<View style={{flexDirection: 'row', height: 40}}>
            <BottomButton
              title="全部已读"
              handleClick={this.handleAllRead}
            />

            <View style={{width: 1/PixelRatio.get(), height: 40, backgroundColor: 'white'}}/>

            <BottomButton
              title="删除"
              handleClick={this.handleDelete}
            />
          </View>)
          : null}
      </View>
    );
  }


  handleEdit = () => {
    this._updateMessageList();
    this.setState({
      isEditing: !this.state.isEditing
    });
  };

  handleBackClick = () => {
    if (this.state.isEditing) {
      this._updateMessageList();
      this.setState({
        isEditing: false,
      });
    } else {
      this.props.navigation.goBack();
    }
  };

  /**
   * 发送
   * @private
   */
  _updateMessageList = () => {
    DeviceEventEmitter.emit(Constant.EVENT_UPDATE_MESSAGE_LIST, !this.state.isEditing); //发监听
  };

  handleAllRead = () => {
    ToastAndroid.show('handleAllRead', ToastAndroid.SHORT)
  };

  handleDelete = () => {
    ToastAndroid.show('handleDelete', ToastAndroid.SHORT)
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column'
  },
  button: {
    color: 'white',
  },
  buttonWrapper: {
    flex: 1,
    paddingLeft: 10,
    paddingRight: 10,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: theme.activeColor,
  },
  btnContainer: {
    flex: 1,
  }
});

//https://reactnavigation.org/docs/intro/nesting
MessageWrapperScreen.router = MessageNavigator.router;

扩展:【解决ReactNavigation中Navigator嵌套问题

MessageScreen.js

import React from 'react';
import {
  StyleSheet,
  View,
  FlatList,
  ActivityIndicator,
  ToastAndroid,
} from 'react-native';

import LoadingView from "../components/LoadingView";
import MessageItem from "../components/MessageItem";
import {requestMessageList} from "../request/Apis";

export default class MessageScreen extends React.Component {

  constructor(props) {
    super(props);
    this.page = 1;
    this.state = {
      first: false,//首次加载,不渲染FlatList
      refreshing: false,
      loadMore: false,
      dataList: [],
      isEditing: false,
    }
  }

  componentDidMount() {
    // this.requestData(1)
  }

  _onPressItem = (msgId) => {
    const {navigate} = this.props.navigation;
    console.log(this.props.navigation);
    navigate('MessageDetails', {'msgId': msgId})
  };

  _renderItem = (item, index) => (
    <MessageItem
      editable = {this.state.isEditing}
      data={item}
      onPressItem={this._onPressItem}
    />
  );

  _dividerLine = () => (<View style={{height: 2}}/>);

  _emptyComponent = () => (<LoadingView/>);

  _onRefresh = () => {
    this.setState({
      refreshing: true
    });
    this.requestData(1)
  };

  _onEndReached = () => {
    if (!this.state.refreshing && !this.state.loadMore) {
      this.setState({
        loadMore: true
      });
      this.requestData(++this.page);
    }
  };

  _onFooterComponent = () => {
    return this.state.loadMore ? (
      <View style={styles.footerStyle}>
        <ActivityIndicator
          style={styles.indicatorStyle}
          size="large"
        />
      </View>
    ) : null
  };

  requestData = () => {
    requestMessageList().then((result) => {
      this.setState({
        first: false,
        loadMore: false,
        refreshing: false,
        dataList: this.page === 1 ? result.data : this.state.dataList.concat(result.data),
      })
    }, (error) => {
      this.setState({
        first: false,
        loadMore: false,
        refreshing: false,
      });
      ToastAndroid.show("error", ToastAndroid.SHORT);
    });
  };

  render() {

    let content;

    if (this.state.first) {
      content = (<LoadingView/>);
    } else {
      content = (<FlatList
        initialNumToRender={10}
        data={this.state.dataList}
        keyExtractor={(item, index) => item.relationId}
        renderItem={this._renderItem}
        onRefresh={this._onRefresh}
        refreshing={this.state.refreshing}
        ItemSeparatorComponent={this._dividerLine}
        onEndReachedThreshold={0.1}
        onEndReached={this._onEndReached}
        ListFooterComponent={this._onFooterComponent}
      />);
    }

    return (
      <View style={{flex: 1}}>
        {content}
      </View>
    );
  }

  // render(){
  //   return(<View/>)
  // }
}

const styles = StyleSheet.create({
  footerStyle: {
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#DDD'
  },
  indicatorStyle: {
    height: 40,
    alignItems: 'center',
    justifyContent: 'center'
  }
});

MessageItem.js

import React from 'react';
import {StyleSheet, View, Text, TouchableNativeFeedback,
  Image, Dimensions,DeviceEventEmitter, ToastAndroid} from 'react-native';

import moment from 'moment';
import CheckBox from './CheckBox';
import Constant from "../Constant";

export default class MessageItem extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      checked: false,
      isEditing: false,
    }
  }

  _onPress = () => {
    const item = this.props.data.item;
    this.props.onPressItem(item.relationId);
  };

  componentWillMount() {
    this.listener = DeviceEventEmitter.addListener(Constant.EVENT_UPDATE_MESSAGE_LIST, (e) => {
      this.setState({
        isEditing: e
      });
    });
  }

  onChange = (checked) => {
    this.setState({
      checked: !this.state.checked,
    });
  };

  componentWillUnmount() {
    this.listener.remove();
  }

  render() {
    const item = this.props.data.item;
    const time = moment(item.gmtCreate).format('YYYY-MM-DD');

    return (
      <TouchableNativeFeedback
        onPress={this._onPress}
        background={TouchableNativeFeedback.Ripple('#CCC', false)}>
        <View style={styles.itemContainer}>
          {this.state.isEditing ?
            (<View style={{marginRight: 5}}>
              <CheckBox
                label=''
                checked={this.state.checked}
                onChange={this.onChange}
              />
            </View>)
            : null}
          <View style={{flex: 1}}>
            <View style={styles.itemTitleContainer}>
              <View style={{flexDirection: 'row', alignItems: 'center', flexShrink: 1,}}>
                {item.isRead == 0 ? <View style={styles.dot}/> : null}
                <Text style={styles.titleStyle} numberOfLines={1}>{item.title}</Text>
              </View>
              <Text style={[styles.contentStyle, {flexShrink: 0}]}>{time}</Text>
            </View>

            <Text style={[styles.contentStyle, {marginTop: 6}]} numberOfLines={2}>{item.summarize}</Text>
          </View>
        </View>
      </TouchableNativeFeedback>
    );
  }
}

const styles = StyleSheet.create({
  itemContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#FFF',
    padding: 8,
  },
  itemTitleContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  titleStyle: {
    fontSize: 15,
    color: '#222',
  },
  contentStyle: {
    fontSize: 13,
    color: '#BBB',
  },
  dot: {
    width: 6,
    height: 6,
    borderRadius: 3,
    backgroundColor: '#FF0000',
    marginRight: 3
  },
});

核心代码

在MessageItem中添加时间监听,并在组件卸载后移除监听

  componentWillMount() {
    this.listener = DeviceEventEmitter.addListener(
      Constant.EVENT_UPDATE_MESSAGE_LIST, (e) => {
      this.setState({
        isEditing: e
      });
    });
  }

  componentWillUnmount() {
    this.listener.remove();
  }

点击编辑按钮,触发事件

DeviceEventEmitter.emit(
    Constant.EVENT_UPDATE_MESSAGE_LIST, !this.state.isEditing); //发监听
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青菜小王子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值