React Native todo app with Redux

Pawel Grajewski
4 min readOct 31, 2019

This is another story in discovering how react-native can be integrated with react tools. In this example, we will use redux and in the second scenario react-redux to maintain add and delete elements in the state.

Let’s create a basic redux project to quickly explain how it works:

npm install --save redux

and then we can start creating our code:

import React, { Component } from 'react'
import { Text } from 'react-native'
import { createStore } from 'redux'
// Define action types
const types = {
INCREMENT: 'INCREMENT',
}
// Define a reducer
const reducer = (state, action) => {
if (action.type === types.INCREMENT) {
return {count: state.count + 1}
}
return state
}
// Define the initial state of our store
const initialState = {count: 0}
// Create a store, passing our reducer function and our initial state
const store = createStore(reducer, initialState)
/// We're done! Redux is all set up. Here's how we can use it:// Now we can dispatch actions, which are understood by our store/reducerstore.dispatch({type: types.INCREMENT})
store.dispatch({type: types.INCREMENT})
store.dispatch({type: types.INCREMENT})
// Calling `store.getState()` returns our state objectexport default function App() {
return (
<Text style={{fontSize: 100}}>
{store.getState().count}
</Text>
)
}

Now when we know how to use the redux let’s create with it todo app.

The basic structure of our store:

In our reducer, we always return the copy of the fresh state and todos.

App.js with basic logic:

import React from 'react'
import { View } from 'react-native'
import { actionCreators } from './todoListRedux'
import List from './List'
import Input from './Input'
import Title from './Title'
import store from './store'export default class App extends React.Component { state = {} unsubscribe = store.subscribe(() => {
const {todos} = store.getState()
this.setState({todos})
})
componentWillMount() {
const {todos} = store.getState()
this.setState({todos})
}
componentWillUnmount() {
unsubscribe()
}
onAddTodo = (text) => {
store.dispatch(actionCreators.add(text))
}
onRemoveTodo = (index) => {
store.dispatch(actionCreators.remove(index))
}
render() {
const {todos} = this.state
return (
<View>
<Title>
To-Do List
</Title>
<Input
placeholder={'Type a todo, then hit enter!'}
onSubmitEditing={this.onAddTodo}
/>
<List
list={todos}
onPressItem={this.onRemoveTodo}
/>
</View>
)
}
}

Let’s see how the calling function looks like:

And rest of the code:

store.jsimport { createStore } from 'redux'
import { reducer } from './todoListRedux'
// Create a store with our reducer
const store = createStore(reducer)
export default store

Now let’s see the reducer:

todoListRedux.js// The types of actions that you can dispatch to modify the state of the store
export const types = {
ADD: 'ADD',
REMOVE: 'REMOVE',
}
// Helper functions to dispatch actions, optionally with payloads
export const actionCreators = {
add: item => {
return { type: types.ADD, payload: item }
},
remove: index => {
return { type: types.REMOVE, payload: index }
},
}
// Initial state of the store
const initialState = {
todos: ['Click to remove', 'Learn React Native', 'Write Code', 'Ship App'],
}
// Function to handle actions and update the state of the store.
// Notes:
// - The reducer must return a new state object. It must never modify
// the state object. State objects should be treated as immutable.
// - We set \`state\` to our \`initialState\` by default. Redux will
// call reducer() with no state on startup, and we are expected to
// return the initial state of the app in this case.
export const reducer = (state = initialState, action) => {
const { todos } = state
const { type, payload } = action
switch (type) {
case types.ADD: {
return {
...state,
todos: [payload, ...todos],
}
}
case types.REMOVE: {
return {
...state,
todos: todos.filter((todo, i) => i !== payload),
}
}
}
return state
}

And our List.js, Input.js, Title.js components:

List.jsimport React, { Component } from 'react'
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native'
export default class List extends Component {
renderItem = (text, i) => {
const { onPressItem } = this.props
return (
<TouchableOpacity style={styles.item} onPress={() => onPressItem(i)}>
<Text>{text}</Text>
</TouchableOpacity>
)
}
render() {
const { list } = this.props
return <View>{list.map(this.renderItem)}</View>
}
}
const styles = StyleSheet.create({
item: {
backgroundColor: 'whitesmoke',
marginBottom: 5,
padding: 15,
},
})
Input.jsimport React, { Component } from 'react'
import { TextInput, StyleSheet } from 'react-native'
export default class Input extends Component {
state = {
text: '',
}
onChangeText = text => this.setState({ text })onSubmitEditing = () => {
const { onSubmitEditing } = this.props
const { text } = this.state
if (!text) return // Don't submit if emptyonSubmitEditing(text)
this.setState({ text: '' })
}
render() {
const { placeholder } = this.props
const { text } = this.state
return (
<TextInput
style={styles.input}
value={text}
placeholder={placeholder}
onChangeText={this.onChangeText}
onSubmitEditing={this.onSubmitEditing}
/>
)
}
}
const styles = StyleSheet.create({
input: {
padding: 15,
height: 50,
},
})
Title.jsimport React, { Component } from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default class Title extends Component {
render() {
const { children } = this.props
return (
<View style={styles.header}>
<Text style={styles.title}>{children}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
header: {
backgroundColor: 'skyblue',
padding: 15,
},
title: {
textAlign: 'center',
color: 'white',
},
})

And we have a basic concept of how redux can be used to react native app.

Book to read:

Fullstack React Native: The complete guide to React Native!

--

--