@Blog
過去の蓄積を見るために書く日記.
React Native + SupabaseでTodoアプリ開発コピペ🙅
文字数[8217文字] この記事は10分16秒で読めます.
おはようございます.React Native + SupabaseでTodoアプリ開発を彼のソース・コードを参考にしてコードを書きました.コードの修正版をUPします.コード量がブログに記載するには多いので今回はコードだけをUPしますのでライブラリのインストールなどはご自身でインストールしてください.
import React, { useState } from 'react';
import { Alert, StyleSheet, View, TextInput, Button } from 'react-native';
import { supabase } from '../lib/supabase';
export default function Auth() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
async function signInWithEmail() {
setLoading(true);
const { error } = await supabase.auth.signInWithPassword({ email, password });
if (error) Alert.alert(error.message);
setLoading(false);
}
async function signUpWithEmail() {
setLoading(true);
const { error } = await supabase.auth.signUp({ email, password });
if (error) Alert.alert(error.message);
setLoading(false);
}
return (
<View style={styles.container}>
<TextInput style={styles.inputContainer} onChangeText={setEmail} value={email} placeholder="email@address.com" autoCapitalize="none" placeholderTextColor="#8B949E" />
<TextInput style={styles.inputContainer} onChangeText={setPassword} value={password} secureTextEntry={true} placeholder="Password" placeholderTextColor="#8B949E" autoCapitalize="none" />
<Button title="Sign in" disabled={loading} onPress={signInWithEmail} />
<View style={{ marginTop: 10 }}>
<Button title="Sign up" disabled={loading} onPress={signUpWithEmail} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#7a7c80',
padding: 20,
paddingTop: 60,
},
inputContainer: {
flexDirection: 'row',
marginBottom: 20,
backgroundColor: '#7a7c80',
borderRadius: 12,
paddingHorizontal: 10,
paddingVertical: 8,
alignItems: 'center',
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 6,
elevation: 5,
color:'#fff',
},
});import React, { useState, useEffect, createContext, useContext, PropsWithChildren } from 'react';
import { Session } from '@supabase/supabase-js';
import { supabase } from '../lib/supabase';
type AuthContextType = { session: Session | null; };
const AuthContext = createContext<AuthContextType>({ session: null });
export const AuthProvider = ({ children }: PropsWithChildren) => {
const [session, setSession] = useState<Session | null>(null);
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
setSession(session);
});
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
setSession(session);
});
return () => subscription.unsubscribe();
}, []);
return <AuthContext.Provider value={{ session }}>{children}</AuthContext.Provider>;
};
export const useAuth = () => useContext(AuthContext);import React, { useState, useEffect } from 'react';
import { View, TextInput, Button, FlatList, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { supabase } from '../lib/supabase';
import { useAuth } from './AuthProvider';
type Todo = { id: number; task: string; is_complete: boolean; };
export default function TodoList() {
const { session } = useAuth();
const [todos, setTodos] = useState<Todo[]>([]);
const [newTask, setNewTask] = useState('');
useEffect(() => {
fetchTodos();
}, []);
const fetchTodos = async () => {
const { data, error } = await supabase.from('todos').select('*').order('id');
if (error) console.log('error', error);
else setTodos(data as Todo[]);
};
const addTodo = async () => {
if (!newTask.trim()) return;
const { data, error } = await supabase.from('todos').insert({ task: newTask, user_id: session!.user.id }).select();
if (error) console.log('error', error);
else setTodos([...todos, data[0]]);
setNewTask('');
};
const toggleComplete = async (id: number, is_complete: boolean) => {
const { error } = await supabase.from('todos').update({ is_complete: !is_complete }).match({ id });
if (error) console.log('error', error);
else setTodos(todos.map(todo => todo.id === id ? { ...todo, is_complete: !is_complete } : todo));
};
const deleteTodo = async (id: number) => {
const { error } = await supabase.from('todos').delete().match({ id });
if (error) console.log('error', error);
else setTodos(todos.filter(todo => todo.id !== id));
};
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<TextInput style={styles.input} onChangeText={setNewTask} value={newTask} placeholder="Add a new task" placeholderTextColor="#8B949E" />
<Button title="Add" onPress={addTodo} />
</View>
<FlatList
data={todos}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.todoItem}>
<TouchableOpacity onPress={() => toggleComplete(item.id, item.is_complete)}>
<Text style={item.is_complete ? styles.completed : {color:'#fff'}}>{item.task}</Text>
</TouchableOpacity>
<Button title="Delete" onPress={() => deleteTodo(item.id)} color="red" />
</View>
)}
/>
<Button title="Sign Out" onPress={() => supabase.auth.signOut()} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#7a7c80',
padding: 20,
paddingTop: 60,
},
inputContainer: {
flexDirection: 'row',
marginBottom: 20,
backgroundColor: '#7a7c80',
borderRadius: 12,
paddingHorizontal: 10,
paddingVertical: 8,
alignItems: 'center',
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 6,
elevation: 5,
color:'#fff',
},
input: {
flex: 1,
color: '#fff',
fontSize: 16,
marginRight: 8,
},
todoItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#7a7c80',
padding: 14,
borderRadius: 10,
marginBottom: 10,
shadowColor: '#000',
shadowOpacity: 0.15,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 4,
elevation: 3,
},
completed: {
textDecorationLine: 'line-through',
color: '#58A6FF',
opacity: 0.6,
},
});import 'react-native-url-polyfill/auto';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createClient } from '@supabase/supabase-js';
import { SUPABASE_URL, SUPABASE_ANON_KEY } from '@env';
const supabaseUrl = SUPABASE_URL;
const supabaseAnonKey = SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
storage: AsyncStorage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,
},
});declare module '@env' {
export const SUPABASE_URL: string;
export const SUPABASE_ANON_KEY: string;
}module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
[
'module:react-native-dotenv',
{
moduleName: '@env',
path: '.env',
safe: false,
allowUndefined: true,
},
],
],
};
};
SUPABASE_URL=先ほど控えたURL
SUPABASE_ANON_KEY=先ほど控えたanonキー明日へ続く
3562番目の投稿です/1154 回表示されています.
中の人🏠️
AIによるおすすめ記事
著者名
@taoka_toshiaki
※この記事は著者が40代後半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
最近よく読まれている記事(過去3ヶ月)
タグ
Native, react, Supabase, Todo, UP, アプリ, インストール, コード, ソース, ブログ, ライブラリ, 今回, 修正, 参考, 明日, 自身, 記載, 開発,

コメントを残す