Skip to content

docs: add Redux example #291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions examples/redux/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { StyleSheet, View, StatusBar } from 'react-native';
import { Provider } from 'react-redux';
import configureStore from './store';
import AddTodo from './components/AddTodo';
import TodoList from './components/TodoList';

const store = configureStore();

export default function App() {
return (
<Provider store={store}>
<View style={styles.container}>
<StatusBar barStyle="dark-content" />
<AddTodo />
<TodoList />
</View>
</Provider>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 32,
},
});
25 changes: 25 additions & 0 deletions examples/redux/actions/todoActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const actions = {
ADD: '@ADD_TODO',
REMOVE: '@REMOVE_TODO',
MODIFY: '@MODIFY_TODO',
CLEAR: '@CLEAR_TODO',
};

export const addTodo = (todo) => ({
type: actions.ADD,
payload: todo,
});

export const removeTodo = (id) => ({
type: actions.REMOVE,
payload: { id },
});

export const modifyTodo = (todo) => ({
type: actions.MODIFY,
payload: todo,
});

export const clearTodos = () => ({
type: actions.CLEAR,
});
6 changes: 6 additions & 0 deletions examples/redux/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['module:metro-react-native-babel-preset'],
};
};
73 changes: 73 additions & 0 deletions examples/redux/components/AddTodo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { Button, StyleSheet, Text, View, TextInput } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { addTodo } from '../actions/todoActions';

export function AddTodo(props) {
const [text, setText] = React.useState('');

const submitForm = () => {
const todo = {
id: props.todoLength + 1,
text,
date: new Date(),
};

props.addTodo(todo);
setText('');
};

return (
<View style={styles.container}>
<Text style={styles.header}>Enter a text below to add a new todo</Text>
<TextInput
autoFocus
value={text}
style={styles.input}
returnKeyType="search"
onSubmitEditing={submitForm}
onChangeText={(t) => setText(t)}
placeholder="Enter the name of the repository here"
/>

<Button onPress={submitForm} title="Submit form" />
</View>
);
}

const styles = StyleSheet.create({
container: {
minHeight: 156,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
borderBottomColor: '#EEEEEE',
borderBottomWidth: 2,
marginBottom: 16,
padding: 16,
},
header: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 8,
},
input: {
borderColor: '#DDDDDD',
borderWidth: 1,
paddingVertical: 8,
width: '100%',
textAlign: 'center',
borderRadius: 4,
},
});

const mapStateToProps = ({ todos }) => ({
todoLength: todos.length,
});

const mapDispatchToProps = (dispatch) =>
bindActionCreators({ addTodo }, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);
42 changes: 42 additions & 0 deletions examples/redux/components/AddTodo.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { Provider } from 'react-redux';
import { cleanup, fireEvent, render } from 'react-native-testing-library';
import configureStore from '../store';
import AddTodo from './AddTodo';

describe('Application test', () => {
afterEach(cleanup);

test('adds a new test when entry has been included', () => {
const store = configureStore();

const component = (
<Provider store={store}>
<AddTodo />
</Provider>
);

const { getByPlaceholder, getByText } = render(component);

const input = getByPlaceholder(/repository/i);
expect(input).toBeTruthy();

const textToEnter = 'This is a random element';
fireEvent.changeText(input, textToEnter);
fireEvent.press(getByText('Submit form'));

const todosState = store.getState().todos;

expect(todosState.length).toEqual(1);

expect(todosState).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: 1,
text: textToEnter,
date: expect.any(Date),
}),
])
);
});
});
25 changes: 25 additions & 0 deletions examples/redux/components/TodoElem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { StyleSheet, Button, Text, View } from 'react-native';

export default function TodoElem({ todo, onDelete }) {
return (
<View style={styles.container}>
<View style={styles.textContainer}>
<Text style={styles.text}>{todo.text}</Text>
<Text style={styles.date}>{new Date(todo.date).toDateString()}</Text>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => onDelete(todo.id)} title="Delete" />
</View>
</View>
);
}

const styles = StyleSheet.create({
container: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
});
29 changes: 29 additions & 0 deletions examples/redux/components/TodoList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { FlatList, Text } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { removeTodo } from '../actions/todoActions';
import TodoElem from './TodoElem';

export function TodoList(props) {
const onDeleteTodo = (id) => props.removeTodo(id);

return (
<FlatList
data={props.todos}
keyExtractor={(todo) => todo.id.toString()}
renderItem={({ item }) => (
<TodoElem todo={item} onDelete={onDeleteTodo} />
)}
/>
);
}

const mapStateToProps = (state) => ({
todos: state.todos,
});

const mapDispatchToProps = (dispatch) =>
bindActionCreators({ removeTodo }, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
59 changes: 59 additions & 0 deletions examples/redux/components/TodoList.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { Provider } from 'react-redux';
import { cleanup, fireEvent, render } from 'react-native-testing-library';
import configureStore from '../store';
import TodoList from './TodoList';

describe('Application test', () => {
afterEach(cleanup);

test('it should execute with a store with 4 elements', () => {
const initialState = {
todos: [
{ id: 1, text: 'Sing something', date: new Date() },
{ id: 2, text: 'Dance something', date: new Date() },
{ id: 3, text: 'Sleep something', date: new Date() },
{ id: 4, text: 'Sleep something', date: new Date() },
],
};
const store = configureStore(initialState);

const component = (
<Provider store={store}>
<TodoList />
</Provider>
);

const { getAllByText } = render(component);
const todoElems = getAllByText(/something/i);

expect(todoElems.length).toEqual(4);
});

test('should execute with 2 elements and end up with 1 after delete', () => {
const initialState = {
todos: [
{ id: 1, text: 'Sing something', date: new Date() },
{ id: 2, text: 'Dance something', date: new Date() },
],
};
const store = configureStore(initialState);

const component = (
<Provider store={store}>
<TodoList />
</Provider>
);

const { getAllByText } = render(component);
const todoElems = getAllByText(/something/i);

expect(todoElems.length).toBe(2);

const buttons = getAllByText('Delete');
expect(buttons.length).toBe(2);

fireEvent.press(buttons[0]);
expect(getAllByText('Delete').length).toBe(1);
});
});
8 changes: 8 additions & 0 deletions examples/redux/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { registerRootComponent } from 'expo';

import App from './App';

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
26 changes: 26 additions & 0 deletions examples/redux/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "redux-example",
"description": "Testing Redux interactions with RNTL",
"version": "0.0.1",
"scripts": {
"test": "jest"
},
"dependencies": {
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-redux": "^7.2.0",
"redux": "^4.0.5"
},
"devDependencies": {
"@babel/core": "^7.8.6",
"babel-jest": "~25.2.6",
"jest": "~25.2.6",
"metro-react-native-babel-preset": "^0.59.0",
"react-native-testing-library": "^1.13.2",
"react-test-renderer": "~16.9.0"
},
"private": true,
"jest": {
"preset": "react-native"
}
}
6 changes: 6 additions & 0 deletions examples/redux/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { combineReducers } from 'redux';
import todos from './todoReducer';

export default combineReducers({
todos,
});
28 changes: 28 additions & 0 deletions examples/redux/reducers/todoReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { actions } from '../actions/todoActions.js';

export default function todoReducer(state = [], action) {
switch (action.type) {
case actions.ADD:
return state.concat(action.payload);

case actions.REMOVE:
console.log(action);
return state.filter((todo) => todo.id !== action.payload.id);

case actions.MODIFY:
return state.map((todo) => {
if (todo.id === action.payload.id) {
return {
...todo,
...action.payload,
};
}
});

case actions.CLEAR:
return [];

default:
return state;
}
}
10 changes: 10 additions & 0 deletions examples/redux/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createStore } from 'redux';
import reducers from './reducers';

const initialStore = {
todos: [],
};

export default function configureStore(initialState = initialStore) {
return createStore(reducers, initialState);
}