Redux
Redux is all about having some central store that managing our entire application state or where we store our entire application state.
Get GITHUB code from HERE.
Getting Started
- You will have to install and configure React Native. If you don’t know how then you can visit to my this tutorial .
- Create a new react native application ReactReduxExample .Use this command:
react-native init ReactReduxExample
Integrating Redux
To integrate redux in our application we have to install redux and react-redux in our root folder.
cd ReactReduxExample npm install - - save redux npm install - - save react-redux
or
npm install - - save redux react-redux
Design
We will cover 5 basic React/Redux concepts. In three sections:
- Actions
- Stores/Reducers
- Containers and Components
Actions
Actions are payloads of information that send data from your application to your store.
Actions are plain JavaScript objects. Actions must have a type property that indicates the type of action being performed.
ActionTypes.js
export const ADD_PLACE = 'ADD_PLACE';
export const DELETE_PLACE = 'DELETE_PLACE';
export const SELECT_PLACE = 'SELECT_PLACE';
export const DESELECT_PLACE = 'DESELECT_PLACE';
Actions
{
type: ADD_PLACE,
placeName: placeName
}
{
type: DELETE_PLACE
}
{
type: SELECT_PLACE,
placeKey: key
}
{
type: DESELECT_PLACE
}
Action Creators
Action creators are exactly that—functions that create actions or return actions.
ActionCreator (places.js)
import { ADD_PLACE, DELETE_PLACE, SELECT_PLACE, DESELECT_PLACE } from './actionTypes';
export const addPlace = (placeName) => {
return {
type: ADD_PLACE,
placeName: placeName
};
};
export const deletePlace = () => {
return {
type: DELETE_PLACE
};
};
export const selectPlace = (key) => {
return {
type: SELECT_PLACE,
placeKey: key
};
};
export const deselectPlace = () => {
return {
type: DESELECT_PLACE
};
};
Reducers
Reducers receive the dispatched actions from the component and corresponding to that action it updates the application state in a pure synchronous function.
The reducer is a pure function that takes the previous state and an action, and returns the new state.
(previousState, action) => newState
Reducer(Places.js)
import {
ADD_PLACE,
DELETE_PLACE,
SELECT_PLACE,
DESELECT_PLACE
} from "../actions/actionTypes";
const initialState = {
places: [],
selectedPlace: null
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case ADD_PLACE:
return {
...state,
places: state.places.concat({
key: Math.random(),
name: action.placeName,
image: {
uri:
"https://c1.staticflickr.com/5/4096/4744241983_34023bf303_b.jpg"
}
})
};
case DELETE_PLACE:
return {
...state,
places: state.places.filter(place => {
return place.key !== state.selectedPlace.key;
}),
selectedPlace: null
};
case SELECT_PLACE:
return {
...state,
selectedPlace: state.places.find(place => {
return place.key === action.placeKey;
})
};
case DESELECT_PLACE:
return {
...state,
selectedPlace: null
};
default:
return state;
}
};
export default reducer;
Store
The Store is the object that holds our entire application state, Allows access to state via getState() , Allows state to be updated via dispatch(action) etc.
configureStore.js
import { createStore, combineReducers } from 'redux';
import placesReducer from './reducers/places';
const rootReducer = combineReducers({
places: placesReducer
});
const configureStore = () => {
return createStore(rootReducer);
};
export default configureStore;
We can have more than one reducers in our application ,we used combineReducers() to combine several reducers into one. combineReducers() returns a reducer(function) that invokes every reducer inside the reducers object.
Here we called it as rootReducer.Now we will pass it to createStore().
createStore() returns an object(Store) that holds the complete state of your app.
Passing the Store
<Provider> magically make the store available to all container components in the application without passing it explicitly as a prop to every container component. You only need to use it once when you render the root component:
index.js
import React from 'react';
import { AppRegistry } from 'react-native';
import { Provider } from 'react-redux';
import App from './App';
import configureStore from './src/store/configureStore';
const store = configureStore();
const RNRedux = () => (
<Provider store={store}>
<App />
</Provider>
);
AppRegistry.registerComponent('ReactReduxExample', () => RNRedux);
Components
Components are responsible for rendering data to the screen.
Containers
Container components are components that are connected with redux. They are responsible for retrieving data, and in order to get that data, the component needs to use Redux’s connect and mapStateToProps functions.
App.js
import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
import { connect } from "react-redux";
import PlaceInput from "./src/components/PlaceInput/PlaceInput";
import PlaceList from "./src/components/PlaceList/PlaceList";
import PlaceDetail from "./src/components/PlaceDetail/PlaceDetail";
import {
addPlace,
deletePlace,
selectPlace,
deselectPlace
} from "./src/store/actions/index";
class App extends Component {
placeAddedHandler = placeName => {
this.props.onAddPlace(placeName);
};
placeDeletedHandler = () => {
this.props.onDeletePlace();
};
modalClosedHandler = () => {
this.props.onDeselectPlace();
};
placeSelectedHandler = key => {
this.props.onSelectPlace(key);
};
render() {
return (
<View style={styles.container}>
<PlaceDetail
selectedPlace={this.props.selectedPlace}
onItemDeleted={this.placeDeletedHandler}
onModalClosed={this.modalClosedHandler}
/>
<PlaceInput onPlaceAdded={this.placeAddedHandler} />
<PlaceList
places={this.props.places}
onItemSelected={this.placeSelectedHandler}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 26,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "flex-start"
}
});
const mapStateToProps = state => {
return {
places: state.places.places,
selectedPlace: state.places.selectedPlace
};
};
const mapDispatchToProps = dispatch => {
return {
onAddPlace: name => dispatch(addPlace(name)),
onDeletePlace: () => dispatch(deletePlace()),
onSelectPlace: key => dispatch(selectPlace(key)),
onDeselectPlace: () => dispatch(deselectPlace())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
App Layout
We’ll add all our app related code in a new folder called ‘src‘ in the root of our newly created project. Create these folders and files I have created inside src directory as shown below with respective code :
ListItem.js
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity, Image } from "react-native";
const listItem = props => (
<TouchableOpacity onPress={props.onItemPressed}>
<View style={styles.listItem}>
<Image resizeMode="cover" source={props.placeImage} style={styles.placeImage} />
<Text>{props.placeName}</Text>
</View>
</TouchableOpacity>
);
const styles = StyleSheet.create({
listItem: {
width: "100%",
marginBottom: 5,
padding: 10,
backgroundColor: "#eee",
flexDirection: "row",
alignItems: "center"
},
placeImage: {
marginRight: 8,
height: 30,
width: 30
}
});
export default listItem;
PlaceDetail.js
import React from "react";
import { Modal, View, Image, Text, Button, StyleSheet } from "react-native";
const placeDetail = props => {
let modalContent = null;
if (props.selectedPlace) {
modalContent = (
<View>
<Image source={props.selectedPlace.image} style={styles.placeImage} />
<Text style={styles.placeName}>{props.selectedPlace.name}</Text>
</View>
);
}
return (
<Modal
onRequestClose={props.onModalClosed}
visible={props.selectedPlace !== null}
animationType="slide"
>
<View style={styles.modalContainer}>
{modalContent}
<View>
<Button title="Delete" color="red" onPress={props.onItemDeleted} />
<Button title="Close" onPress={props.onModalClosed} />
</View>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
modalContainer: {
margin: 22
},
placeImage: {
width: "100%",
height: 200
},
placeName: {
fontWeight: "bold",
textAlign: "center",
fontSize: 28
}
});
export default placeDetail;
PlaceInput.js
import React, { Component } from "react";
import { View, TextInput, Button, StyleSheet } from "react-native";
class PlaceInput extends Component {
state = {
placeName: ""
};
componentDidMount() {
}
placeNameChangedHandler = val => {
this.setState({
placeName: val
});
};
placeSubmitHandler = () => {
if (this.state.placeName.trim() === "") {
return;
}
this.props.onPlaceAdded(this.state.placeName);
};
render() {
return (
<View style={styles.inputContainer}>
<TextInput
placeholder="An awesome place"
value={this.state.placeName}
onChangeText={this.placeNameChangedHandler}
style={styles.placeInput}
/>
<Button
title="Add"
style={styles.placeButton}
onPress={this.placeSubmitHandler}
/>
</View>
);
}
}
const styles = StyleSheet.create({
inputContainer: {
// flex: 1,
width: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
},
placeInput: {
width: "70%"
},
placeButton: {
width: "30%"
}
});
export default PlaceInput;
PlaceList.js
import React from "react";
import { StyleSheet, FlatList } from "react-native";
import ListItem from "../ListItem/ListItem";
const placeList = props => {
return (
<FlatList
style={styles.listContainer}
data={props.places}
renderItem={(info) => (
<ListItem
placeName={info.item.name}
placeImage={info.item.image}
onItemPressed={() => props.onItemSelected(info.item.key)}
/>
)}
/>
);
};
const styles = StyleSheet.create({
listContainer: {
width: "100%"
}
});
export default placeList;