Skip to content

Commit f773a68

Browse files
author
Moses Esan
committed
Tutorial 2a Done, Needs Testing
0 parents  commit f773a68

19 files changed

+8182
-0
lines changed

.expo-shared/assets.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true,
3+
"89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true
4+
}

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
node_modules/**/*
2+
.expo/*
3+
npm-debug.*
4+
*.jks
5+
*.p8
6+
*.p12
7+
*.key
8+
*.mobileprovision
9+
*.orig.*
10+
web-build/
11+
web-report/

.watchmanconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

App.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { StyleSheet, Text, View } from 'react-native';
3+
4+
export default function App() {
5+
return (
6+
<View style={styles.container}>
7+
<Text>Open up App.js to start working on your app!</Text>
8+
</View>
9+
);
10+
}
11+
12+
const styles = StyleSheet.create({
13+
container: {
14+
flex: 1,
15+
backgroundColor: '#fff',
16+
alignItems: 'center',
17+
justifyContent: 'center',
18+
},
19+
});

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Tutorial 2a: How to add CRUD operations to your React Native app using React Hooks and Redux
2+
3+
### Demo
4+
![Demo](https://github.com/MosesEsan/mesan-react-native-hooks-redux-boilerplate/blob/master/demo.gif "demo")
5+
6+
### Tutorial
7+
<ul>
8+
<li><a href="#step4">Step 4: Create the Actions</a></li>
9+
<li><a href="#step5">Step 5: Create the Reducer</a></li>
10+
</ul>
11+
12+
### FOLLOW Step 1 to 3 in <a href="https://github.com/MosesEsan/mesan-react-native-hooks-redux-boilerplate/tree/tutorial_1c">Tutorial 1c</a>
13+
14+
<a name="step4"></a>
15+
### Step 4: Create the Actions
16+
17+
<b>ADD_QUOTE</b><br>
18+
This action is the CREATE operation, the new quote is passed to the addQuote function.
19+
20+
<b>QUOTES_AVAILABLE</b><br>
21+
This action will act as the READ operation, the quotes are passed to the addQuotes function.
22+
23+
<b>UPDATE_QUOTE</b><br>
24+
This action is the UPDATE operation, the updated quote is passed to the updateQuote function. 
25+
26+
<b>DELETE_QUOTE</b><br>
27+
This action is the DELETE operation, the deleted quote id is passed to the deleteQuote function.
28+
29+
30+
<a name="step5"></a>
31+
### Step 5: Create the Reducer
32+
33+
<b>ADD_QUOTE</b><br>
34+
The state 'quotes' variable is cloned and the new quote is pushed to the top of the cloned object, the state 'quotes' variable is replaced with the clone object.
35+
36+
<b>QUOTES_AVAILABLE</b><br>
37+
The state 'quotes' variable is updated with the quotes array dispatched to the reducer.
38+
39+
<b>UPDATE_QUOTE</b><br>
40+
The state 'quotes' variable is cloned, the id of the quote dispatched to the reducer is used to find the index of the quote in the cloned object. 
41+
The quote at that index is replaced with the quote dispatched to the reducer. The state 'quotes' variable is replaced with the clone object.
42+
43+
<b>DELETE_QUOTE</b><br>
44+
The state 'quotes' variable is cloned, the id dispatched to the reducer is used to find the index of the quote in the cloned object.
45+
The quote at that index is removed and the state 'quotes' variable is replaced with the clone object.
46+
47+
### FOLLOW Step 7 in <a href="https://github.com/MosesEsan/mesan-react-native-hooks-redux-boilerplate/tree/tutorial_1c">Tutorial 1c</a> to create the Redux Store.
48+
49+
50+
51+
The rest of the tutorial is available on my <a href="" target="_blank">blog</a>.

app.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"expo": {
3+
"name": "React Native with Hooks, Redux",
4+
"slug": "react-native-redux-crud",
5+
"privacy": "public",
6+
"sdkVersion": "35.0.0",
7+
"platforms": [
8+
"ios",
9+
"android",
10+
"web"
11+
],
12+
"version": "1.0.0",
13+
"orientation": "portrait",
14+
"icon": "./assets/icon.png",
15+
"splash": {
16+
"image": "./assets/splash.png",
17+
"resizeMode": "contain",
18+
"backgroundColor": "#ffffff"
19+
},
20+
"updates": {
21+
"fallbackToCacheTimeout": 0
22+
},
23+
"assetBundlePatterns": [
24+
"**/*"
25+
],
26+
"ios": {
27+
"supportsTablet": true
28+
}
29+
}
30+
}

app/actions.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export const QUOTES_AVAILABLE = 'QUOTES_AVAILABLE';
2+
export const ADD_QUOTE = 'ADD_QUOTE';
3+
export const UPDATE_QUOTE = 'UPDATE_QUOTE';
4+
export const DELETE_QUOTE = 'DELETE_QUOTE';
5+
6+
// Get Quotes
7+
export const addQuotes = (quotes) => ({
8+
type: QUOTES_AVAILABLE,
9+
data: {quotes}
10+
});
11+
12+
// Add Quote - CREATE (C)
13+
export const addQuote = (quote) => ({
14+
type: ADD_QUOTE,
15+
data: {quote}
16+
});
17+
18+
// Update Quote - UPDATE (U)
19+
export const updateQuote = (quote) => ({
20+
type: UPDATE_QUOTE,
21+
data: {quote}
22+
});
23+
24+
// Delete Quote - DELETE (D)
25+
export const deleteQuote = (id) => ({
26+
type: DELETE_QUOTE,
27+
data: {id}
28+
});

app/components/home.js

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import React, {useEffect, useState} from 'react';
2+
import { FlatList, StyleSheet, View, Text, ActivityIndicator, TouchableHighlight, Platform, ActionSheetIOS, Animated} from 'react-native';
3+
import { useDispatch, useSelector } from 'react-redux';
4+
import {RectButton} from 'react-native-gesture-handler';
5+
import Swipeable from 'react-native-gesture-handler/Swipeable';
6+
7+
import axios from 'axios'; //Only import if using api
8+
9+
import { addQuotes, deleteQuote } from "../actions";
10+
11+
//Buttons for Action Sheet
12+
const BUTTONS = [
13+
"Edit",
14+
"Delete",
15+
'Cancel',
16+
];
17+
const CANCEL_INDEX = 2;
18+
19+
export default function Home(props) {
20+
const dispatch = useDispatch();
21+
const { navigation } = props;
22+
23+
//1 - DECLARE VARIABLES
24+
const [isFetching, setIsFetching] = useState(false);
25+
26+
//Access Redux Store State
27+
const dataReducer = useSelector((state) => state.dataReducer);
28+
const { quotes } = dataReducer;
29+
30+
//==================================================================================================
31+
32+
//2 - MAIN CODE BEGINS HERE
33+
useEffect(() => getData(), []);
34+
35+
//==================================================================================================
36+
37+
//3 - GET FLATLIST DATA
38+
const getData = () => {
39+
setIsFetching(true);
40+
41+
let url = "https://my-json-server.typicode.com/mesandigital/demo/quotes";
42+
axios.get(url)
43+
.then((res) => dispatch(addQuotes(res)))
44+
.catch(error => alert(error.message))
45+
.finally(() => setIsFetching(false));
46+
};
47+
48+
//==================================================================================================
49+
50+
//4 - RENDER FLATLIST ITEM
51+
const renderItem = ({item, index}) => {
52+
return (
53+
<Swipeable
54+
renderLeftActions={(progress, dragX) => renderLeftActions(progress, dragX, item)}>
55+
<View style={styles.row}>
56+
<Text style={styles.quote}>
57+
{item.quote}
58+
</Text>
59+
<Text style={styles.author}>
60+
{item.author}
61+
</Text>
62+
</View>
63+
</Swipeable>
64+
)
65+
};
66+
67+
renderLeftActions = (progress, dragX, quote) => {
68+
const trans = dragX.interpolate({
69+
inputRange: [0, 50, 100, 101],
70+
outputRange: [-20, 0, 0, 1],
71+
});
72+
73+
const onPress = () => deleteWithQuoteId(quote.id);
74+
return (
75+
<View style={{ width: 192, flexDirection: 'row' }}>
76+
<RectButton style={styles.leftAction} onPress={onPress}>
77+
<Animated.Text
78+
style={[
79+
styles.actionText,
80+
{
81+
transform: [{ translateX: trans }],
82+
},
83+
]}>
84+
Delete
85+
</Animated.Text>
86+
</RectButton>
87+
{/*{this.renderRightAction('More', '#C8C7CD', 192, progress)}*/}
88+
{/*{this.renderRightAction('Flag', '#ffab00', 128, progress)}*/}
89+
{/*{this.renderRightAction('More', '#dd2c00', 64, progress)}*/}
90+
</View>
91+
92+
93+
);
94+
};
95+
96+
//==================================================================================================
97+
98+
//5 - SHOW ACTION SHEET
99+
const showOptions = (quote) => {
100+
if (Platform.OS === 'ios'){
101+
ActionSheetIOS.showActionSheetWithOptions({
102+
options: BUTTONS,
103+
cancelButtonIndex: CANCEL_INDEX,
104+
destructiveButtonIndex: 1,
105+
},
106+
(buttonIndex) => {
107+
if (buttonIndex === 0){
108+
navigation.navigate('NewQuote', {
109+
quote: quote,
110+
title:"Edit Quote"
111+
});
112+
}else if (buttonIndex === 1) deleteWithQuoteId(quote.id)
113+
});
114+
}
115+
};
116+
117+
//==================================================================================================
118+
119+
//6 - DELETE QUOTE
120+
const deleteWithQuoteId = (id) => {
121+
setIsFetching(true);
122+
123+
let url = "https://my-json-server.typicode.com/mesandigital/demo/quotes";
124+
axios.delete(url, {id})
125+
.then((res) => {
126+
127+
alert("back");
128+
console.log(res)
129+
dispatch(deleteQuote(id))
130+
})
131+
.catch(error => alert(error.message))
132+
.finally(() => setIsFetching(false));
133+
};
134+
135+
//==================================================================================================
136+
137+
//5 - RENDER
138+
if (isFetching) {
139+
return (
140+
<View style={styles.activityIndicatorContainer}>
141+
<ActivityIndicator animating={true}/>
142+
</View>
143+
);
144+
} else{
145+
return (
146+
<View style={{flex:1, backgroundColor: '#F5F5F5', paddingTop:20}}>
147+
<FlatList
148+
data={quotes}
149+
renderItem={renderItem}
150+
keyExtractor={(item, index) => `quotes_${index}`}/>
151+
152+
<TouchableHighlight style={styles.floatingButton}
153+
underlayColor='#ff7043' onPress={() => Actions.new_quote()}>
154+
<Text style={{fontSize: 25, color: 'white'}}>+</Text>
155+
</TouchableHighlight>
156+
</View>
157+
);
158+
}
159+
};
160+
161+
const styles = StyleSheet.create({
162+
activityIndicatorContainer:{
163+
backgroundColor: "#fff",
164+
alignItems: 'center',
165+
justifyContent: 'center',
166+
flex: 1,
167+
},
168+
169+
floatingButton:{
170+
backgroundColor: '#ff5722',
171+
borderColor: '#ff5722',
172+
borderWidth: 1,
173+
height: 50,
174+
width: 50,
175+
borderRadius: 50 / 2,
176+
alignItems: 'center',
177+
justifyContent: 'center',
178+
position: 'absolute',
179+
bottom: 20,
180+
right: 20,
181+
shadowColor: "#000000",
182+
shadowOpacity: 0.8,
183+
shadowRadius: 2,
184+
shadowOffset: {
185+
height: 1,
186+
width: 0
187+
}
188+
},
189+
190+
row:{
191+
borderBottomWidth: 1,
192+
borderColor: "#ccc",
193+
padding: 10
194+
},
195+
196+
author: {
197+
fontSize: 14,
198+
fontWeight: "600",
199+
marginTop: 8 * 2
200+
},
201+
202+
quote: {
203+
marginTop: 5,
204+
fontSize: 14,
205+
},
206+
207+
leftAction: {
208+
flex: 1,
209+
backgroundColor: '#497AFC',
210+
justifyContent: 'center',
211+
},
212+
actionText: {
213+
color: 'white',
214+
fontSize: 16,
215+
backgroundColor: 'transparent',
216+
padding: 10,
217+
},
218+
rightAction: {
219+
alignItems: 'center',
220+
flex: 1,
221+
justifyContent: 'center',
222+
},
223+
});

0 commit comments

Comments
 (0)