-
Notifications
You must be signed in to change notification settings - Fork 203
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add react native tutorial, fix some mistakes found along the way
- Loading branch information
Vincent (Wen Yu) Ge
committed
Apr 7, 2024
1 parent
af0bd5c
commit 39f47ad
Showing
17 changed files
with
718 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<script lang="ts"> | ||
import { globToTutorial } from '$lib/utils/tutorials.js'; | ||
import { setContext } from 'svelte'; | ||
export let data; | ||
const tutorials = globToTutorial(data); | ||
setContext('tutorials', tutorials); | ||
</script> | ||
|
||
<slot /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import type { LayoutLoad } from './$types'; | ||
|
||
export const load: LayoutLoad = ({ url }) => { | ||
const tutorials = import.meta.glob('./**/*.markdoc', { | ||
eager: true | ||
}); | ||
return { | ||
tutorials, | ||
pathname: url.pathname | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { redirect } from '@sveltejs/kit'; | ||
import type { PageLoad } from './$types'; | ||
|
||
export const load: PageLoad = async () => { | ||
redirect(303, '/docs/tutorials/react/step-1'); | ||
}; |
28 changes: 28 additions & 0 deletions
28
src/routes/docs/tutorials/react-native/step-1/+page.markdoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
--- | ||
layout: tutorial | ||
title: Build an ideas tracker with React Native | ||
description: Learn to build a React Native app with no backend code using an Appwrite backend. | ||
step: 1 | ||
difficulty: beginner | ||
readtime: 10 | ||
category: Mobile and native | ||
framework: React Native | ||
--- | ||
|
||
**Idea tracker**: an app to track all the side project ideas that you'll start, but probably never finish. | ||
In this tutorial, you will build Idea tracker with Appwrite and React Native. | ||
|
||
# Concepts {% #concepts %} | ||
|
||
This tutorial will introduce the following concepts: | ||
|
||
1. Setting up your first project | ||
2. Authentication | ||
3. Databases and collections | ||
4. Queries and pagination | ||
|
||
# Prerequisites {% #prerequisites %} | ||
|
||
1. Android, iOS simulators, or a physical device to run the app | ||
2. Have [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer | ||
3. Basic knowledge of React Native and Expo |
43 changes: 43 additions & 0 deletions
43
src/routes/docs/tutorials/react-native/step-2/+page.markdoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
--- | ||
layout: tutorial | ||
title: Create app | ||
description: Create a React Native app project and integrate with Appwrite. | ||
step: 2 | ||
--- | ||
|
||
# Create React Native project {% #create-react-project %} | ||
|
||
Create a React Native app with the `npm create` command. | ||
|
||
```sh | ||
npx create-expo-app ideas-tracker | ||
cd ideas-tracker | ||
``` | ||
|
||
# Add dependencies {% #add-dependencies %} | ||
|
||
Install the React Native Appwrite SDK. | ||
|
||
```sh | ||
npx expo install react-native-appwrite react-native-url-polyfill | ||
``` | ||
|
||
Then, install React Navigation to help implement simple navigation logic. | ||
|
||
```sh | ||
npm install @react-navigation/native @react-navigation/native-stack | ||
``` | ||
|
||
Install peer dependencies needed for React Navigation. | ||
|
||
```sh | ||
npx expo install react-native-screens react-native-safe-area-context | ||
``` | ||
|
||
For iOS with bare React Native project, make sure you have CocoaPods installed. Then install the pods to complete the installation: | ||
|
||
``` | ||
cd ios | ||
pod install | ||
cd .. | ||
``` |
60 changes: 60 additions & 0 deletions
60
src/routes/docs/tutorials/react-native/step-3/+page.markdoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
--- | ||
layout: tutorial | ||
title: Set up Appwrite | ||
description: Import and initialize Appwrite for your React Native application. | ||
step: 3 | ||
--- | ||
|
||
# Create project {% #create-project %} | ||
|
||
Head to the [Appwrite Console](https://cloud.appwrite.io/console). | ||
|
||
{% only_dark %} | ||
![Create project screen](/images/docs/quick-starts/dark/create-project.png) | ||
{% /only_dark %} | ||
{% only_light %} | ||
![Create project screen](/images/docs/quick-starts/create-project.png) | ||
{% /only_light %} | ||
|
||
If this is your first time using Appwrite, create an account and create your first project. | ||
|
||
Then, under **Add a platform**, add a **Android** or **Apple** platform with the package/bundle ID `com.example.idea-tracker`. | ||
|
||
{% only_dark %} | ||
![Add a platform](/images/docs/quick-starts/dark/add-platform.png) | ||
{% /only_dark %} | ||
{% only_light %} | ||
![Add a platform](/images/docs/quick-starts/add-platform.png) | ||
{% /only_light %} | ||
|
||
You can skip optional steps. | ||
|
||
# Initialize Appwrite SDK {% #init-sdk %} | ||
|
||
To use Appwrite in our React Native app, you'll need to find our project ID. | ||
Find your project's ID in the **Settings** page. | ||
|
||
{% only_dark %} | ||
![Project settings screen](/images/docs/quick-starts/dark/project-id.png) | ||
{% /only_dark %} | ||
{% only_light %} | ||
![Project settings screen](/images/docs/quick-starts/project-id.png) | ||
{% /only_light %} | ||
|
||
Create a new file `lib/appwrite.js` to hold our Appwrite related code. | ||
Only one instance of the `Client()` class should be created per app. | ||
Add the following code to it, replacing `<YOUR_PROJECT_ID>` with your project ID. | ||
|
||
```js | ||
import { Client, Databases, Account } from "react-native-appwrite"; | ||
|
||
const client = new Client(); | ||
client | ||
.setEndpoint("https://cloud.appwrite.io/v1") | ||
.setProject("<YOUR_PROJECT_ID>") // Replace with your project ID | ||
.setPlatform('com.example.idea-tracker'); | ||
|
||
|
||
export const account = new Account(client); | ||
export const databases = new Databases(client); | ||
``` |
166 changes: 166 additions & 0 deletions
166
src/routes/docs/tutorials/react-native/step-4/+page.markdoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
--- | ||
layout: tutorial | ||
title: Add authentication | ||
description: Add authentication to your React Native application. | ||
step: 4 | ||
--- | ||
|
||
# User context {% #user-context %} | ||
|
||
In React Native, you can use [context](https://reactjs.org/docs/context.html) to share data between components. | ||
You can use a context and a custom hook to manage our user's data. | ||
|
||
Create a new file `contexts/UserContext.jsx` and add the following code to it. | ||
|
||
```js | ||
import { ID } from "react-native-appwrite"; | ||
import { createContext, useContext, useEffect, useState } from "react"; | ||
import { account } from "../lib/appwrite"; | ||
import { toast } from "../lib/toast"; | ||
|
||
|
||
const UserContext = createContext(); | ||
|
||
export function useUser() { | ||
return useContext(UserContext); | ||
} | ||
|
||
export function UserProvider(props) { | ||
const [user, setUser] = useState(null); | ||
|
||
async function login(email, password) { | ||
const loggedIn = await account.createEmailSession(email, password); | ||
setUser(loggedIn); | ||
toast('Welcome back. You are logged in'); | ||
} | ||
|
||
async function logout() { | ||
await account.deleteSession("current"); | ||
setUser(null); | ||
toast('Logged out'); | ||
} | ||
|
||
async function register(email, password) { | ||
await account.create(ID.unique(), email, password); | ||
await login(email, password); | ||
toast('Account created'); | ||
} | ||
|
||
async function init() { | ||
try { | ||
const loggedIn = await account.get(); | ||
setUser(loggedIn); | ||
toast('Welcome back. You are logged in'); | ||
} catch (err) { | ||
setUser(null); | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
init(); | ||
}, []); | ||
|
||
return ( | ||
<UserContext.Provider value={{ current: user, login, logout, register, toast }}> | ||
{props.children} | ||
</UserContext.Provider> | ||
); | ||
} | ||
``` | ||
|
||
Now, you can use the `useUser` hook to access the user's data from any component wrapped by this context's provider. | ||
|
||
# Display toasts {% #display-toasts %} | ||
|
||
For a better user experience, display toasts when the users perform an action, such as login, logout, create new ideas, etc. | ||
|
||
We can do this by creating a new file `lib/toast.js` and adding the following code to it. | ||
|
||
```js | ||
import { ToastAndroid, Platform, AlertIOS } from 'react-native'; | ||
|
||
export function toast(msg) { | ||
if (Platform.OS === 'android') { | ||
ToastAndroid.show(msg, ToastAndroid.SHORT) | ||
} else { | ||
AlertIOS.alert(msg); | ||
} | ||
} | ||
``` | ||
# Login page {% #login-page %} | ||
|
||
Create a new file `views/Login.jsx` and add the following code to it. | ||
this page contains a basic form to allow the user to login or register. | ||
Notice how this page consumes the `useUser` hook to access the user's data and perform login and register actions. | ||
|
||
```js | ||
import React, { useState } from 'react'; | ||
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'; | ||
import { useUser } from '../contexts/UserContext'; | ||
|
||
|
||
export default function LoginScreen() { | ||
const user = useUser(); | ||
const [email, setEmail] = useState(''); | ||
const [password, setPassword] = useState(''); | ||
return ( | ||
<View style={styles.container}> | ||
<Text style={styles.header}>Login or register</Text> | ||
<TextInput | ||
style={styles.input} | ||
placeholder="Email" | ||
value={email} | ||
onChangeText={setEmail} | ||
/> | ||
<TextInput | ||
style={styles.input} | ||
placeholder="Password" | ||
value={password} | ||
onChangeText={setPassword} | ||
secureTextEntry | ||
/> | ||
<View style={styles.buttonContainer}> | ||
<Button | ||
title="Login" | ||
onPress={ | ||
() => { | ||
user.login(email, password) | ||
} | ||
} | ||
/> | ||
<Button | ||
title="Register" | ||
onPress={ | ||
() => { | ||
user.register(email, password) | ||
} | ||
} | ||
/> | ||
</View> | ||
</View> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
justifyContent: 'center', | ||
padding: 16, | ||
}, | ||
header: { | ||
fontSize: 24, | ||
marginBottom: 20, | ||
}, | ||
input: { | ||
height: 40, | ||
borderColor: 'gray', | ||
borderWidth: 1, | ||
marginBottom: 10, | ||
paddingLeft: 8, | ||
}, | ||
buttonContainer: { | ||
flexDirection: 'row', | ||
justifyContent: 'space-between', | ||
}, | ||
}); | ||
``` |
Oops, something went wrong.