-
Fork this chapsnat repo!
-
Clone your forked version
-
run
yarn install
to kickstart it (download all the dependencies)- Discuss: why do we need to do this? What exactly is it doing?
-
run
expo start
to make sure it's working. Should just be a "starter" React Native app. -
Take this time to explore the project folder with your partner!
-
add the
react-native-gifted-chat
package to your project- Discuss: how do you do this with
yarn
? - Checkout
package.json
after you add the package. Do you see it there?
- Discuss: how do you do this with
-
Take 5 minutes to checkout the README in the Gifted Chat repo
- https://github.com/FaridSafi/react-native-gifted-chat
- Discuss: Look at the example, and all the props. So many props!
-
Now, using the Example code in the README as a guide, import the GiftedChat component into your
App.js
, and put the component in your render function (which is thereturn
statement). Run the code - try it out! Wow!- Discuss: what is going on? What is the
messages
state? What are themessages
,onSend
, anduser
props? useCallback
is another hook! You can read more about it if you like, but for now just treat it like a normal arrow function callback with a fancy wrapper.- Try removing
setMessages
from the callback and run the code again. What happens? What’s changed?
- Discuss: what is going on? What is the
-
Explore time! Go back to the GiftedChat README. Time to play around with some of those props.
- Change the placeholder text from “Type a message…” to something else!
- Change
user
prop to have your name and a photo (avatar) of your choosing from the web.- You might need to use the
showUserAvatar
prop for the user info to show up
- You might need to use the
- Try using the
alwaysShowSend
prop. What changes? - Try using
renderUsernameOnMessage
! Nice!
-
Time to take a deeper look at the whole
messages
thing!-
First, let’s see how the Example code from the README loads in some “dummy” messages. (What do we mean by “dummy”?)
useEffect(() => { setMessages([ ... ]) }, [])
-
There’s that
useEffect
hook we were talking about! -
Take a few minutes to read through the explanation here: https://reactjs.org/docs/hooks-overview.html#effect-hook
- Take note: that empty array at the end means that the "effect" only gets run once at the beginning when our app loads. (Why do we want this?)
-
Discuss with your partner! There’s a lot of information there, so [✨💪 ACTION ITEM ☑️] come up with a couple questions to share back with class, that we can answer together.
-
Now, start playing around with that “dummy” data! Add some more messages to the array. Create a fake conversation!
- What are the
_id
s?- Which one is the message
_id
and which is the user_id
?
- Which one is the message
- What's going on with
createdAt
? - [🥅 GOAL 🏁] Can you make both grey and blue bubbles in the dummy data?
- Try adding images and/or videos to your dummy messages.
- What are the
-
expo install firebase
The RN app uses Cloud Firestore to save chat messages and receive new chat messages.
You'll need to enable Cloud Firestore with the following steps:
-
In the Firebase console's Develop section, click Database.
-
Click Create database in the Cloud Firestore pane.
-
Select the Start in test mode option, then click Next after reading the disclaimer about the security rules.
Test mode ensures that we can freely write to the database during development. We'll make our database more secure later on in this codelab.
d. Make sure you select your data to be stored close to where you are physically!
us-west2 is LA.
☝🏾 Please be careful to enable Cloud Firestore and NOT the Realtime Database for this codelab. Both options are on the same page, but you need to enable Cloud Firestore, which is in the top section of the page.Yes, it's confusing... We just created a Project, but now we have to create an App under that Project!
Go to the Project Overview Page.
- Select the web app option. Even though we're technically not making a web app, React Native will fall under this category!
b. Register the app with the nickname Smapchat. Click Register app.
c. You'll see some code snippets! We'll copy the Firebase SDK code into our React Native app.
Go back to your React Native project in VSCode. Create a new firebase.js
file:
import firebase from "firebase/app";
import "firebase/firebase-firestore";
// Your web app's Firebase configuration, which you copy-pasted from Step 6
var firebaseConfig = {
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
let firestore = firebase.firestore();
export default firestore;
On the lefthand bar of the Firebase Console, click on Cloud Firestore.
Start a collection, and call it Chats
:
Then we'll add the first document:
Keep this link handy for reference when doing Firestore stuff (make sure to choose "Node.js" in the code examples sections):
First, make sure you've completed the lab from yesterday. You should have a firebase.js
file in your chapsnat folder, with your firebaseConfig
details. You should also have a Chats collection in your online Firebase/Firestore console, and that collection should have at least one "chat document" that looks something like this:
Make sure you and your partner are both at this stage!
import firebase from "firebase/app";
Get data with Cloud Firestore | Firebase
-
First, import your Firestore from yesterday into the
App.js
file:import db from "./firebase";
-
Now, let's clear our
useEffect
function of all the dummy data we added, and add in some new code to retrieve data from our database:useEffect(() => { db.collection("Chats") .doc("myfirstchat") .get() .then((snapshot) => { console.log(snapshot.id); console.log(snapshot.data()); }); }, []);
- Discuss with your partner what's going here!
.collection
,.doc
,.get
, and.then
. Look at that callback there! And also make sure to run the code and look in your console to see what you get!
- Discuss with your partner what's going here!
-
Next, let's tweak this
useEffect
code to actually take that data we downloaded and use it in our app withsetMessages
. Discuss with your partner where thesetMessages
call should go, and what should be in it. -
[✅ CHECKPOINT] You should have your dummy message from Firestore appear in your app! wow!
- Add another message into your chat doc in firestore, and make sure it appears in your app!
- Try to get at least one message downloaded from firestore to be a blue bubble! Remember that the
_id
needs to match. Watchout! The number 1 and the string "1" are not an exact match, for example. - NB: you might see "Invalid Date". Don't worry about that for now, we'll fix it later.
Add data to Cloud Firestore | Firebase
First! Beware that we're about to potentially lose all our dummy messages in a terrible accident! Create a new chat document and use that for this section if you don't want to lose them.
-
Let's add some code to the beginning of
onSend
callback to upload our new messages to Firestore:db.collection("Chats").doc("myfirstchat").set({ messages: messages });
- Discuss again! What is
.set
doing? Why don't we need.then
?
- Discuss again! What is
-
Send a message and now go to your Firestore database dashboard
- [✅ CHECKPOINT] Wow! You should see the message appear in your dashboard.
- BUT your old messages disappeared!
-
And now send a few new messages, and refresh your React Native app page, as well as your Firestore database dashboard page.
-
UH OH!!! What's the bug you're seeing?
-
Answer:
Only the latest message gets saved! We lose all the others.
-
-
[✨💪 ACTION ITEM ☑️] Discuss, then call an instructor or coach over and discuss why you think you're seeing this bug
-
Add a
console.log(messages)
at the beginning ofonSend
callback and start to debug what's going on. -
[🥅 GOAL 🏁] Now let's try to fix it!
You absolutely need to try to do this on your own before looking at the answer! It is there to compare your work to, not to copy. Take a peek at the hints right away if need be.
Hints:
- might need to use
.update
instead of.set
- 👀 https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
- You'll need to
import firebase from "firebase/app"
at the top ofApp.js
in order tofirebase.firestore.FieldValue.arrayUnion(...)
Answer:
const onSend = useCallback((messages = []) => { db.collection("Chats") .doc("myfirstchat") .update({ // arrayUnion appends the message to the existing array messages: firebase.firestore.FieldValue.arrayUnion(messages[0]), }); setMessages((previousMessages) => GiftedChat.append(previousMessages, messages) ); }, []);
-
Get realtime updates with Cloud Firestore | Firebase
This one is actually surprisingly easy! Let's revisit our useEffect
function.
- All we do is take this code:
db.collection("Chats")
.doc("myfirstchat")
.get()
.then((snapshot) => {
...
}
- And replace it with this code:
db.collection("Chats")
.doc("myfirstchat")
.onSnapshot((snapshot) => {
console.log("New Snapshot!");
...
}
-
[✅ CHECKPOINT] Great! Now when you send messages, you should see "New Snapshot!" in your console.
-
[✅ CHECKPOINT] Now if you add a message in your Firestore database dashboard, it should appear immediately in your app without having to refresh the app.
-
There's one last thing to do. At some point we want to stop listening to updates. This is called "unsubscribing".
-
Q: when do we want to stop listening? A: when we exit the chat view!
-
Q: How do we know when we're exiting the chat view? A: Whenever a component gets "unmounted" (fancy React word for taken off screen), it runs a "cleanup" function.
-
Q: ummm what? A: you can read more about unsubscribing and clean up functions here:
-
Finally, your
useEffect
code should look something like this:useEffect(() => { let unsubscribeFromNewSnapshots = db .collection("Chats") .doc("myfirstchat") .onSnapshot((snapshot) => { console.log("New Snapshot!"); setMessages(snapshot.data().messages); }); return function cleanupBeforeUnmounting() { unsubscribeFromNewSnapshots(); }; }, []);
-
Snap-Engineering-Academy-2021/chapsnat
To help us learn about the different types of Navigation in React, we'll be exploring 3 mini-projects in the browser, using Expo Snacks. Expo Snack works kind of like Replit but specifically for React Native code; it allows you to quickly view, run, and change React Native code in the browser.
Click on the link below and click around the app in the right hand side. Open up the drawer navigation!
- [✨💬 DISCUSS] The side bar should link to both "Home" and "Notifications"! Can you find the line in the code where the Drawer Navigator is being defined?
- [✨💬 DISCUSS] Can you find the lines in the code where HomeScreen is being defined? It should be a functional component (aka a function that returns a component).
- [✨💬 DISCUSS] Can you find the line in the code where NotificationsScreen is being defined? It should be a functional component (aka a function that returns a component).
Now we're going to look at the EXACT same code ...except this time you'll see that some of it was moved into a SCREENS folder.
-
[✨💬 DISCUSS] Why would we do this??? Why would we bother moving some of the code from App.js into a separate folder?
-
Answer
Decomposition! This way our App.js is a lot less cluttered. It only handles the high-level navigation now.
-
-
[✨💬 DISCUSS] Now let's do the same scavenger hunt as above: Can you find the line in the code where the Drawer Navigator is being defined?
-
Answer
It's in the
App.js
file! This is going to be the parent component of our entire app. It's the first file that gets rendered, and it will be in charge of importing the other files/screens that will get rendered.
-
-
[✨💬 DISCUSS] Can you find the file where HomeScreen is being defined? It should be a functional component (aka a function that returns a component).
-
Answer
It's in the
screens/HomeScreen.js
! Notice however that in order forApp.js
to use this screen, it needs to be imported at the very top ofApp.js
as well.
-
-
[✨💬 DISCUSS] Can you find the file where NotificationsScreen is being defined? It should be a functional component (aka a function that returns a component).
-
Answer
It's in the
screens/NotificationsScreen.js
! Notice however that in order forApp.js
to use this screen, it needs to be imported at the very top ofApp.js
as well.
-
-
[✨💪 ACTION ITEM ☑️ ] Currently, there's no way for you to open the navigation drawer from the Notifications Screen. That's a bummer! Add a button to
screens/NotificationsScreen.js
so that you can open the side bar from the Notifications Screen, the same way you can from the Home Screen.-
Code to check your work
Make sure you talk to your teammates and instructors first before peeking here!
//screens/NotificationsScreen.js import * as React from 'react'; import { Text, View, StyleSheet, Image, Button } from 'react-native'; export default function NotificationsScreen({ navigation }) { return ( <View style={styles.screenContainer}> <Text style={styles.screenText}> Notifications! </Text> <Button title="Open side bar" onPress={() => navigation.openDrawer()} /> </View> ); } const styles = StyleSheet.create({ screenContainer: { flex: 1, alignItems: 'center', justifyContent: 'center' }, screenText: { fontSize: 32, }, });
-
Tab Navigators are where it's at! We definitely want one of these in our Chapsnat
repo. Let's click on this Snack and see what we find:
- [✨💪 ACTION ITEM ☑️ ] There's an error!!! What's up with that? 👀 Debug the
App.js
file so thatHomeScreen
,DetailsScreen
, andSettingsScreen
are properly imported!
-
Code to check your work
Make sure you talk to your teammates and instructors first before peeking here!
import HomeScreen from './screens/HomeScreen'; import SettingsScreen from './screens/SettingsScreen'; import DetailsScreen from './screens/DetailsScreen';
-
[✨💪 ACTION ITEM ☑️ ] You may notice that
SettingsScreen.js
function looks slightly different fromHomeScreen.js
function ... can you spot the difference?- They're actually two different ways of exporting a functional component! Change
SettingsScreen
to match the way thatHomeScreen
andDetailsScreen
are being defined... just for consistency's sake!
- They're actually two different ways of exporting a functional component! Change
-
Code to check your work
Make sure you talk to your teammates and instructors first before peeking here!
import * as React from 'react'; import { Text, View, StyleSheet, Image } from 'react-native'; function SettingsScreen() { return ( <View style={styles.screenContainer}> <Text style={styles.screenText}>Settings!</Text> </View> ); } const styles = StyleSheet.create({ screenContainer: {x flex: 1, justifyContent: 'center', alignItems: 'center', }, screenText: { fontSize: 32, }, }); export default SettingsScreen;
🌴Optional Resources
- React native crash course: https://www.reactnative.express/
- React Navigation documentation for route.params https://reactnavigation.org/docs/params
🌴Optional Resources
- Firebase documentation for currUser, signInWithEmailAndPassword, createUserWithEmailAndPassword: https://firebase.google.com/docs/auth/web/manage-users and https://firebase.google.com/docs/auth/web/password-auth
Nice guide to React as a framework