0

我完成了 React Native 课程,并且正在尝试制作一个聊天应用程序来练习。

总结问题:

  • 我有 2 个屏幕,ContactList.jsChatRoom.js
  • 我有一个带有这两个屏幕Navigation.js的导航堆栈
  • App.js中导入和呈现 Navigation 组件
  • 我添加了 FCM 模块来处理通知

目标是在应用程序收到前台状态通知时执行在聊天室_loadMessages()中加载消息的函数。并执行函数(我还没有创建它)以全局状态更新未读消息。

我试过的

我关注了 react native firebase docs,我有一个函数可以处理在App.js中声明的前台通知。问题是我无法告诉其他组件(屏幕)执行它们的功能。不能使用“Ref”方法,因为我没有直接在App.js中调用子组件(屏幕),而是调用并渲染Navigation.js堆栈。

那么,在这种情况下,当我们在 app.js 上调用导航组件时,我们如何告诉其他组件执行在它们内部声明的函数呢?

应用程序.js

import React, { useEffect } from 'react'
import Navigation from './Navigation/Navigation'
import messaging from '@react-native-firebase/messaging';

export default function App() {

  requestUserPermission = async () => {
    //On récupere le token
    const token = await messaging().getToken();
    console.log('TOKEN: ' + token)
    const authStatus = await messaging().requestPermission();
    const enabled =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL;

    if (enabled) {
      console.log('Authorization status:', authStatus);
    }
  }

  handleForegroundNotification = () => {
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      console.log('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });
    return unsubscribe;
  }

  useEffect(() => {
    this.requestUserPermission();
    this.handleForegroundNotification();
  }, []);

  return (
    <Navigation />
  )
}

导航.js

import { createAppContainer } from "react-navigation"
import { createStackNavigator } from "react-navigation-stack"
import ContactList from '../Components/ContactList'
import ChatRoom from '../Components/ChatRoom'

const ChatStackNavigator = createStackNavigator({
    ContactList: {
        screen: ContactList,
        navigationOptions: {
            title: 'Contacts'
        }
    },
    ChatRoom: {
        screen: ChatRoom,
        navigationOptions: {
            title: 'Conversation'
        }

    }
})

export default createAppContainer(ChatStackNavigator)

聊天室.js

import React from 'react'
import { View, StyleSheet, Text, Image, SafeAreaView, TextInput, Alert, FlatList, ActivityIndicator } from 'react-native'
import { sendMessage } from '../API/sendMessageApi'
import { getMessages } from '../API/getMessagesApi'
import MessageItem from './MessageItem'

class ChatRoom extends React.Component {
    constructor(props) {
        super(props)
        this.message = ""
        this.contact = this.props.navigation.getParam('contact')

        this.state = {
            defautInputValue: "",
            listMessages: [],
            isLoading: true
        }
    }

    _textInputChanged(text) {
        this.message = text
    }
    _sendMessage() {
        this.setState({ defautInputValue: " " });
        sendMessage('1', this.contact.id_contact, this.message).then(() => {
            this._loadMessages();
        });
    }

    _loadMessages() {
        getMessages('1', this.contact.id_contact).then((data) => {
            this.setState({ listMessages: data, isLoading: false, defautInputValue: "" })
        });
    }
    componentDidMount() {
        this._loadMessages();
    }
    _displayLoading() {
        if (this.state.isLoading) {
            return (
                <View style={[styles.loading_container]}>
                    <ActivityIndicator size="large" color="orange" />
                </View>
            )
        }
    }

    render() {
        //console.log('Contact ID: ' + JSON.parse(this.contact))
        return (
            <SafeAreaView style={styles.container}>
                <View style={styles.contact}>
                    <View style={styles.image_container}>
                        <Image
                            style={styles.image}
                            source={{ uri: 'https://moonchat.imedramdani.com/avatar/' + this.contact.avatar }}
                        ></Image>
                    </View>
                    <View style={styles.username_container}>
                        <Text style={styles.username}>{this.contact.username}</Text>
                    </View>
                </View>
                {/* BODY */}
                <View style={styles.body}>
                    <FlatList
                        ref={ref => this.flatList = ref}
                        onContentSizeChange={() => this.flatList.scrollToEnd({ animated: true })}
                        data={this.state.listMessages}
                        keyExtractor={(item) => item.id.toString()}
                        renderItem={({ item }) =>
                            <MessageItem
                                message={item}
                            />}
                    >
                    </FlatList>
                </View>
                <View style={styles.input_container}>
                    <TextInput
                        style={styles.input}
                        onChangeText={(text) => this._textInputChanged(text)}
                        onSubmitEditing={() => this._sendMessage()}
                        defaultValue={this.state.defautInputValue}
                        placeholder="Aa"
                    ></TextInput>
                </View>
                {this._displayLoading()}
            </SafeAreaView>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#1d2733',
    },
    contact: {
        height: 50,
        backgroundColor: '#1d2733',
        marginTop: 0,
        flexDirection: 'row',
        borderBottomColor: 'grey',
        borderWidth: 1
    },
    username_container: {
        //backgroundColor: 'red',
        flex: 3,
        justifyContent: 'center',
        alignItems: 'flex-start'
    },
    username: {
        fontSize: 20,
        color: 'white'
    },
    image_container: {
        //backgroundColor: 'blue',
        flex: 1,
        justifyContent: 'center'
    },
    image: {
        //backgroundColor: 'yellow',
        height: 45,
        width: 45,
        marginLeft: 10,
        borderRadius: 25
    },
    body: {
        //backgroundColor: 'red',
        flex: 1
    },
    input_container: {
        height: 75,
        //backgroundColor:'blue',
        padding: 5
    },
    input: {
        paddingLeft: 20,
        height: 50,
        backgroundColor: 'white',
        borderWidth: 1,
        borderRadius: 25,
        borderColor: '#D5D5D5',
        fontSize: 20
    },
    loading_container: {
        position: 'absolute',
        left: 0,
        right: 0,
        top: 0,
        bottom: 0,
        alignItems: 'center',
        justifyContent: 'center'
    },
});

export default ChatRoom

谢谢!

4

1 回答 1

0

我设置了一个可行的解决方案,但我不知道这是否是正确的方法。

我恢复了App.js

import React from 'react'
import Root from './Root'
import { Provider } from 'react-redux'
import Store from './Store/configureStore'

class App extends React.Component {

  render() {
    return (
      <Provider store={Store}>
        <Root />
      </Provider>
    )
  }
}

export default App

我创建了一个包含通知处理程序的现在组件Root.js

import React from 'react'
import Navigation from './Navigation/Navigation'
import messaging from '@react-native-firebase/messaging'
import { connect } from 'react-redux'

class Root extends React.Component {

    requestUserPermission = async () => {
        //On récupere le token
        const token = await messaging().getToken();
        console.log('TOKEN: ' + token)
        const authStatus = await messaging().requestPermission();
        const enabled =
            authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
            authStatus === messaging.AuthorizationStatus.PROVISIONAL;

        if (enabled) {
            console.log('Authorization status:', authStatus);
        }
    }

    handleForegroundNotification = () => {
        const unsubscribe = messaging().onMessage(async remoteMessage => {
            console.log('A new FCM message arrived!', JSON.stringify(remoteMessage));
            const action = { type: "RELOAD_MESSAGES", value: '1' }
            this.props.dispatch(action)
        });
        return unsubscribe;
    }

    componentDidMount() {
        this.requestUserPermission();
        this.handleForegroundNotification();
    }
    render() {
        return (
            <Navigation />
        )
    }
}

const mapStateToProps = (state) => {
    return {
        loadyourself: state.loadyourself
    }
}

export default connect(mapStateToProps)(Root)

商店在App.js中提供,让Root.js访问全局状态。

当前台收到通知时,Root.js 会更新全局状态中名为“loadyourself”的键。当状态更新时,连接到 store的ChatRoom.js也会触发componentDidUpdate()

componentDidUpdate() {
    if (this.props.loadyourself == "1") {
        this._reloadMessages();
    }
}

当然,为了避免死循环,_reloadMessages()在loadyourself键的全局状态下恢复默认值

_reloadMessages() {
    const action = { type: "RELOAD_MESSAGES", value: '0' }
    this.props.dispatch(action)
    getMessages('1', this.contact.id_contact).then((data) => {
        this.setState({ listMessages: data })
    });
}

消息被更新,全局状态被重新初始化,componentDidUpdate() 直到下一次通知才会触发。

有用。就像我说的,我不知道是否有更合适的方法,我是 React-Native 的新手(2 周)。我对其他解决方案持开放态度

于 2021-01-02T18:39:47.093 回答