I am working with react native maps and react native geocoding, I want to pass the current user location to the redux store using mapDispatchToProps, and to be able to use that location on a different map component. I am roughly a newbie in the redux world and I am a bit confused. kindly make corrections to my code below. I have tried using the example implemented on the documentation, however, I do not fully understand
Location 1
import React, { Component, Fragment } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import { connect } from 'react-redux';
import MapView from 'react-native-maps';
import Geocoder from 'react-native-geocoding';
Geocoder.init('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
const LONGITUDEDELTA = 0.0173;
const LATITUDEDELTA = 0.0174;
class Location1 extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
region: null,
};
this.handleBack = this.handleBack.bind(this);
}
async componentDidMount() {
this.watchId = navigator.geolocation.watchPosition(
async ({ coords: { latitude, longitude } }) => {
const response = await Geocoder.from({ latitude, longitude });
const address = response.results[0].formatted_address;
const location = address.substring(0, address.indexOf(','));
this.setState({
location,
region: {
latitude,
longitude,
title: location,
latitudeDelta: LATITUDEDELTA,
longitudeDelta: LONGITUDEDELTA,
},
});
}, //sucesso
() => {}, //erro
{
timeout: 2000,
enableHighAccuracy: true,
maximumAge: 1000
}
);
}
componentWillUnmount() {
navigator.geolocation.clearWatch(this.watchId);
}
render() {
const { region } = this.state;
const { container, map, } = styles;
return (
<View style={container}>
<MapView
style={map}
region={region}
loadingEnabled
showsCompass
showsTraffic
showsBuildings
showsIndoors
>
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
...StyleSheet.absoluteFill,
},
});
function mapDispatchToProps(dispatch) {
return {
region: () => {
dispatch(this.state.region);
},
location: () => {
dispatch(this.state.location); // address name
}
}
}
export default connect(mapDispatchToProps) (Location1);
Location2
import React, { Component, Fragment } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import { connect } from 'react-redux';
import MapView from 'react-native-maps';
import Geocoder from 'react-native-geocoding';
const LONGITUDEDELTA = 0.0173;
const LATITUDEDELTA = 0.0174;
class Location2 extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
region: null,
};
}
render() {
const { container, map, } = styles;
return (
<View style={container}>
<MapView
style={map}
region={this.props.region}
loadingEnabled
showsCompass
showsTraffic
showsBuildings
showsIndoors
>
</MapView>
<View><Text>{this.props.location}</Text></View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
...StyleSheet.absoluteFill,
},
});
function mapStateToProps(state) {
return {
region: state.region,
location: state.location
}
}
export default connect(mapStateToProps) (Location2);
Reducer
import { CURRENT_LOCATION, LOCATION_NAME } from '../actions/types';
const initialState = {
region: null,
location: ''
};
const Reducer = (state = initialState, action) => {
switch (action.type) {
case CURRENT_LOCATION:
return {
...state,
region: action.region
};
case LOCATION_NAME:
return {
...state,
location: action.location
};
default:
return state;
}
};
export default Reducer;
Firstly in the first code here
function mapDispatchToProps(dispatch) {
return {
region: () => {
dispatch(this.state.region);
},
location: () => {
dispatch(this.state.location); // address name
}
}
}
export default connect(mapDispatchToProps) (Location1);
you need to pass the first argument to connect wich is the mapStateToProps but if you don't wanna use it pass null instead
export default connect(mapStateToProps, mapDispatchToProps)(Location1);
and also you dispatch actions in redux by passing and actionType(required) and a actionPayload(optional)
function mapDispatchToProps(dispatch) {
return {
region: (regionVal) => {
dispatch({type: 'LOCATION_NAME',payload: regionVal});
},
location: (locationVal) => {
dispatch({type: 'CURRENT_LOCATION',payload: locationVal}); // address name
}
}}
and call it inside the componentDidMount like
async componentDidMount() {
.
.
.
this.props.region(regionVal);
this.props.locatio(locationVal);
}
and in the reducer you should fix this
case CURRENT_LOCATION:
return {
...state,
region: action.region
};
to something like this
case CURRENT_LOCATION:
return {
...state,
region: action.payload // you should call the payload from the action
};
Related
I use Redux to set three Counters for my navigation bar. I can access the values in initial state. But I am unable to change the values. My Reducer looks like this:
const SET_FREUNDE = 'SET_FREUNDE';
const SET_CHATS = 'SET_CHATS';
const SET_VOTES = 'SET_VOTES';
export function setFreunde(value) {
return {
type: SET_FREUNDE,
value,
}
}
export function setChats(value) {
return {
type: SET_CHATS,
value,
}
}
export function setVotes(value) {
return {
type: SET_VOTES,
value,
}
}
const defaults =
{
countervotes: 2,
counterchats: 1,
counterfreunde: 1
};
function counter(state=defaults, action) {
switch (action.type) {
case SET_FREUNDE:
return {...state,counterfreunde: action.value}
case SET_CHATS:
return {...state,counterchats: action.value}
case SET_VOTES:
return {...state,countervotes: action.value}
default:{
return state;
}
}
}
export default counter;
Now I want to set the counters on a other screen:
import * as React from "react";
import { Image, StyleSheet,... } from "react-native";
...
import {connect} from 'react-redux';
import { setChats, setFreunde, setVotes } from '../redux/counter';
class ... extends React.Component<{}, State> {
constructor(props) {
super(props);
}
...
render(){
return(
<SafeAreaView style={styles.container}>
<Button onPress={() => setFreunde(2)}/>
...
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
...
});
const mapDispatchToProps = (dispatch, ownProps) => ({
setFreunde: () => {
const { value } = ownProps
dispatch(setFreunde(value))
},
setChats: () => {
const { value } = ownProps
dispatch(setChats(value))
},
setVotes: () => {
const { value } = ownProps
dispatch(setVotes(value))
}
})
export default connect( null, mapDispatchToProps )(NavStack)
If I log the setFreunde(2) the console says the following:
Object { "type": "SET_FREUNDE", "value": 2, }
However, the value does not change which I retrieve in my App.tsx as follows:
const counterfreunde = useSelector((state)=>state.counterfreunde);
What is my mistake?
Problem: Not Dispatching Action
<Button onPress={() => setFreunde(2)}/>
This code right here is just calling the action creator setFreunde. It does not dispatch it.
Your mapDispatchToProps function adds a prop setFreunde to your component which takes no arguments and dispatches the action with the value from props.value. You are not using this prop. You are just using the action creator. You need to call the function from props:
<Button onPress={this.props.setFreunde} title="Set Freunde" />
That fixes the functionality. You do need to add a lot of annotations if you are trying to use typescript here. It's easier with function components!
import { createStore } from "redux";
import { Provider, connect } from "react-redux";
import React, { Dispatch } from "react";
import { SafeAreaView, Button, Text } from "react-native";
const SET_FREUNDE = "SET_FREUNDE";
const SET_CHATS = "SET_CHATS";
const SET_VOTES = "SET_VOTES";
export function setFreunde(value: number) {
return {
type: SET_FREUNDE,
value
};
}
export function setChats(value: number) {
return {
type: SET_CHATS,
value
};
}
export function setVotes(value: number) {
return {
type: SET_VOTES,
value
};
}
const defaults = {
countervotes: 2,
counterchats: 1,
counterfreunde: 1
};
type State = typeof defaults;
type Action = ReturnType<typeof setVotes | typeof setChats | typeof setFreunde>;
function counter(state: State = defaults, action: Action): State {
switch (action.type) {
case SET_FREUNDE:
return { ...state, counterfreunde: action.value };
case SET_CHATS:
return { ...state, counterchats: action.value };
case SET_VOTES:
return { ...state, countervotes: action.value };
default: {
return state;
}
}
}
const store = createStore(counter);
const mapDispatchToProps = (
dispatch: Dispatch<Action>,
ownProps: OwnProps
) => ({
setFreunde: () => {
const { value } = ownProps;
dispatch(setFreunde(value));
},
setChats: () => {
const { value } = ownProps;
dispatch(setChats(value));
},
setVotes: () => {
const { value } = ownProps;
dispatch(setVotes(value));
}
});
const mapStateToProps = (state: State) => ({
freund: state.counterfreunde
});
interface OwnProps {
value: number;
}
type Props = OwnProps &
ReturnType<typeof mapDispatchToProps> &
ReturnType<typeof mapStateToProps>;
class NavStack extends React.Component<Props> {
render() {
return (
<SafeAreaView>
<Text>Freunde Value: {this.props.freund}</Text>
<Button onPress={this.props.setFreunde} title="Set Freunde" />
</SafeAreaView>
);
}
}
const Test = connect(mapStateToProps, mapDispatchToProps)(NavStack);
export default function App() {
return (
<Provider store={store}>
<Test value={5} />
</Provider>
);
}
Code Sandbox Demo
Hello I have a react component like which either display a list of items or opens another component which allowes me to select some value. Here is the code
import React, { PureComponent } from "react";
import { Text, View } from "react-native";
import { withNavigationFocus } from "react-navigation-is-focused-hoc";
import { NavigationActions } from "react-navigation";
import { connect } from "react-redux";
import ClassListing from '../../components/ClassListing/ClassListing';
import Actions from "../../state/Actions";
import { default as stackStyles } from "./styles";
import {
StyleCreator
} from "../../utils";
let styles = StyleCreator(stackStyles.IndexStyles)();
#withNavigationFocus
#connect(() => mapStateToProps, () => mapDispatchToProps)
export default class ClassList extends PureComponent {
static navigationOptions = ({ navigation }) => {
const { params } = navigation.state;
return {
header: null,
tabBarOnPress({ jumpToIndex, scene }) {
params.onTabFocus();
jumpToIndex(scene.index);
},
};
};
constructor(props) {
super(props);
this.state = {};
console.ignoredYellowBox = ["Warning: In next release", "FlatList"];
}
componentDidMount() {
console.log("componentDidMount");
this.props.navigation.setParams({
onTabFocus: this.handleTabFocus
});
}
componentDidUpdate() {
console.log("I am updated");
}
_handleNavigationBar = () => (
<View style={styles.headerContainer}>
<Text style={styles.headerLeftTitle}>Klasselister</Text>
</View>
);
handleTabFocus = () => {
this.props.resetClassList();
// setTimeout(() => {
// this._openChildSchoolSelector();
// },500);
};
_openChildSchoolSelector = () => {
if (
!this.props.childrenSelection.selectedDependant ||
!this.props.childrenSelection.selectedSchool
) {
console.log("navigate to child selector");
this.props.navigation.dispatch(
NavigationActions.navigate({
routeName: "ChildSchoolSelector",
})
);
}
};
render() {
return (
<View>
{this._handleNavigationBar()}
{this.props.classList &&
this.props.classList.classList &&
this.props.classList.classList.length > 0 ? (
<ClassListing list={this.props.classList.classList} />
) : (
null
// this._openChildSchoolSelector()
)}
</View>
);
}
}
const mapStateToProps = (state) => {
const { classList, childrenSelection } = state.appData;
console.log("[Classlist] mapStateToProps", classList);
console.log("[Classlist] mapStateToProps", childrenSelection);
return {
classList,
childrenSelection
};
};
const mapDispatchToProps = (dispatch) => ({
resetClassList: () => {
dispatch(Actions.resetClassList());
},
});
My issue is that when I come to this tab, I want to reset the list which came from server and then open the school selector again, which I am doing in handleTabFocus. But there I have to first call
this.props.resetClassList();
this._openChildSchoolSelector()
The issue is that this._openChildSchoolSelector() is called while this.props.resetClassList() hasn't fininshed.
this.props.resetClassList() works like this
it calls an action like this
static resetClassList() {
return {
type: ActionTypes.RESET_CLASS_LIST
};
}
which then cleans the classlist like this
case ActionTypes.RESET_CLASS_LIST:
return createDefaultState();
in ClassListReducer.js
and also in ChildrenSelectionRedcuer.js
case ActionTypes.RESET_CLASS_LIST:
return createDefaultState();
Any hints to solve this? My project uses very old React navigation v1
One thing which I did was to use this
componentWillReceiveProps(nextProps) {
console.log(this.props);
console.log(nextProps);
if (
nextProps.isFocused &&
nextProps.focusedRouteKey == "ClassList" &&
(!nextProps.childrenSelection.selectedDependant ||
!nextProps.childrenSelection.selectedSchool)
) {
console.log("componentWillReceiveProps");
this._openChildSchoolSelector();
}
if (
this.props.isFocused &&
this.props.focusedRouteKey === "ClassList" &&
nextProps.focusedRouteKey !== "ClassList"
) {
console.log("reset");
this.props.resetClassList();
}
}
But not sure if this is an elegant way of doing this
When I try to enter username and then go on next screen for live chating then I facing this error.
Here is code for ChatScreen.js file.
TypeError: undefined is not a function (near '..._fire.default.get...').
ChatScreen.js
import React,{Component} from "react";
import {Platform,KeyboardAvoidingView} from 'react-native';
import {GiftedChat}from 'react-native-gifted-chat-fix';
import{SafeAreaView}from 'react-native-safe-area-view';
import Video from 'react-native-video';
import Fire from '../fire';
export default class ChatScreen extends Component{
state={
messages:[]
}
get user(){
return{
_id:Fire.uid,
name:this.props.navigation.state.params.name
}
}
componentDidMount(){
Fire.get(message=>this.setState(previous=>({
messages:GiftedChat.append(previous.messages,message)
}))
);
}
componentWillUnmount(){
Fire.off()
}
render(){
const chat=<GiftedChat messages={this.state.messages} onSend={Fire.send} user={this.user}/>;
if(Platform.OS=='android'){
return(
<KeyboardAvoidingView style={{flex:1}}behavior="padding" keyboardVerticalOffset={30} enabled>
{chat}
</KeyboardAvoidingView>
);
}
return<SafeAreaView style={{flex:1}}>{chat}</SafeAreaView>;
}
}
Try changing the code in both files
At first in Fire.js
import firebase from 'firebase'; // 4.8.1
class Fire {
constructor() {
this.init();
this.observeAuth();
}
init = () => {
if (!firebase.apps.length) {
firebase.initializeApp({
apiKey:'AIzaSyAPfes9_2EwZESX1puYMUv29yunzK9Ve5U',
authDomain:'docman-31d96.firebaseapp.com',
databaseURL: "https://docman-31d96.firebaseio.com",
projectId: "docman-31d96",
storageBucket: "docman-31d96.appspot.com",
messagingSenderId: "649332068608",
appId:'1:649332068608:android:08c080ee6a4e521f5323e5'
});
}
};
observeAuth = () =>
firebase.auth().onAuthStateChanged(this.onAuthStateChanged);
onAuthStateChanged = user => {
if (!user) {
try {
firebase.auth().signInAnonymously();
} catch ({ message }) {
alert(message);
}
}
};
get uid() {
return (firebase.auth().currentUser || {}).uid;
}
get ref() {
return firebase.database().ref('messages');
}
parse = snapshot => {
const { timestamp: numberStamp, text, user } = snapshot.val();
const { key: _id } = snapshot;
const timestamp = new Date(numberStamp);
const message = {
_id,
timestamp,
text,
user,
};
return message;
};
on = callback =>
this.ref
.limitToLast(20)
.on('child_added', snapshot => callback(this.parse(snapshot)));
get timestamp() {
return firebase.database.ServerValue.TIMESTAMP;
}
// send the message to the Backend
send = messages => {
for (let i = 0; i < messages.length; i++) {
const { text, user } = messages[i];
const message = {
text,
user,
timestamp: this.timestamp,
};
this.append(message);
}
};
append = message => this.ref.push(message);
// close the connection to the Backend
off() {
this.ref.off();
}
}
Fire.shared = new Fire();
export default Fire;
and then in ChatScreen.js
import * as React from 'react';
import { Platform , KeyboardAvoidingView,SafeAreaView } from 'react-native';
// #flow
import { GiftedChat } from 'react-native-gifted-chat'; // 0.3.0
import Fire from '../fire';
type Props = {
name?: string,
};
class ChatScreen extends React.Component<Props> {
static navigationOptions = ({ navigation }) => ({
title: (navigation.state.params || {}).name || 'Chat!',
});
state = {
messages: [],
};
get user() {
return {
name: this.props.navigation.state.params.name,
_id: Fire.shared.uid,
};
}
render() {
const chat=<GiftedChat messages={this.state.messages} onSend={Fire.shared.send} user={this.user}/>;
if(Platform.OS=='android'){
return(
<KeyboardAvoidingView style={{flex:1}}behavior="padding" keyboardVerticalOffset={0} enabled>
{chat}
</KeyboardAvoidingView>
);
}
return<SafeAreaView style={{flex:1}}>{chat}</SafeAreaView>;
}
componentDidMount() {
Fire.shared.on(message =>
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, message),
}))
);
}
componentWillUnmount() {
Fire.shared.off();
}
}
export default ChatScreen;
This helped for me It should work for you too
To see my chat app just visit https://snack.expo.io/#habibishaikh1/chatapp
I've been working with React/React-Native for a while now, but I'm new to Redux and I cannot find the problem. I have a RESTFull API and two main modules: the service model and the price model. Once the admin user adds a new service the user can also associate a price for that service. The problema is that when I add a service (in the NewServiceScreen) my code dispatches an action to change the redux store and therefore update the service list on the NewPriceScreen for the user to associate a price with the service that was just added.
NewPriceScreen.js
function mapStateToProps(state){
return {
newPrice: state.newPriceReducer
}
}
// Exports the connected NewPriceScreen
export default connect(mapStateToProps)(NewPriceScreen);
NewServiceScreen
...
handleSubmit = async() => {
const { descricao } = this.props.newService;
const userToken = await AsyncStorage.getItem('token');
axios.post('/estabelecimento/servicos/',{descricao: descricao}, {
headers: {
"Authorization": `Token ${userToken}`
}
})
.then(res => {
Alert.alert(
'Deu tudo certo :)',
'Dados salvos com sucesso !',
);
console.log(res.data);
this.props.dispatch({type: 'addService', newService: res.data});
})
.catch(error =>{
Alert.alert(
'Ops ! Algo aconteceu :(',
error.message,
);
})
}
...
const mapStateToProps = state => ({
newService: state.newService
});
export default connect(mapStateToProps)(NewServiceScreen);
Reducers.js
const initialState = {
servicos: [],
// Database variables
servico: 0,
duracao: '',
custo: '',
comissao: ''
}
export default function newPriceReducer(state = initialState, action){
// console.log("My state")
// console.log(state);
switch(action.type){
case 'setState': {
return {
...state,
servico: action.descricao,
duracao: action.duracao,
custo: action.custo,
comissao: action.comissao
}
}
case "ADD_SERVICES": {
// console.log("Servicos");
const newState = {
...state,
servicos: action.servicos
}
// console.log(newState);
// console.log(newState === initialState)
return newState;
}
case 'addService': {
// console.log("ADD Service");
servicos = state.servicos;
servicos.push(action.newService);
const newState = {
...state,
servicos: servicos
}
// console.log(newState);
// console.log(newState === initialState);
return newState
}
default:
return state;
}
}
const initialState = {
descricao: ''
}
export default function newServiceReducer(state = initialState, action){
switch(action.type){
case 'setDescricao': {
return {
...state,
descricao: action.descricao
}
}
default:
return state;
}
}
App.js
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunkMiddleware from 'redux-thunk'
import newServiceReducer from '../reducers/NewService';
import newPriceReducer from "../reducers/NewPrice";
import logger from 'redux-logger';
const mainReducer = combineReducers({
newService: newServiceReducer,
newPriceReducer
})
const store = createStore(mainReducer, applyMiddleware(logger));
export default store
import React from 'react';
import { Platform, StatusBar, StyleSheet, View, AsyncStorage, ImageBackground} from 'react-native';
import { AppLoading, Asset, Font, Icon } from 'expo';
import AppNavigator from './navigation/AppNavigator';
// Redux Stuff
import {Provider} from 'react-redux';
import AppStore from './store/App';
export default class App extends React.Component {
state = {
isLoadingComplete: false,
isLoggedIn: false,
};
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<Provider store={AppStore}>
<ImageBackground source={require('./assets/images/back.png')} style={{width: '100%', height: '100%'}}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</ImageBackground>
</Provider>
);
}
}
}
In lines:
servicos = state.servicos;
servicos.push(action.newService);
I was making some sort mutation. I just changed it to:
const newState = {
...state,
servicos: [...state.servicos, action.newService]
}
I have problem with receiving new props in root stack navigator. I have 2 screens in stack navigator: list and edit item. On list screen i click a edit button and dispatch data to store - it works. But in the edit screen i edit data and dispatch new list with new element (for test). List screen dont receive new list. Can you help me?
App.js
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'CATEGORIES_CHANGED':
return {
...state,
categories: action.data
};
case 'SET_CATEGORY_INFO':
return {
...state,
categoryInfo: action.data
};
default:
return state;
}
};
const store = createStore(reducer);
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppNavigator/>
</Provider>
);
}
}
AppNavigator.js
import React from 'react';
import { createSwitchNavigator } from 'react-navigation';
import MainTabNavigator from './MainTabNavigator';
import { connect } from 'react-redux';
const AppNavigator = createSwitchNavigator({
Main: MainTabNavigator
});
export default AppNavigator;
MainTabNavigator.js
import React from 'react';
import {Platform} from 'react-native';
import {createStackNavigator, createBottomTabNavigator} from 'react-navigation';
import { connect } from 'react-redux';
...
const CategoriesStack = createStackNavigator({
CategoriesListScreen: {
screen: CategoriesListScreen,
},
CategoryInfoScreen: {
screen: CategoryInfoScreen,
},
CategoryEditScreen: {
screen: CategoryEditScreen,
},
});
CategoriesStack.navigationOptions = {
tabBarLabel: 'Categories',
tabBarIcon: ({focused}) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? 'ios-link' : 'md-link'}
/>
),
};
...
const bottomTabNavigator = createBottomTabNavigator({
CategoriesStack,
...
});
export default bottomTabNavigator;
CategoriesListScreen.js
import { connect } from 'react-redux';
class CategoriesListScreen extends React.Component {
render() {
const cats = this.state.categories;
return (
<ScrollView style={styles.container}>
{cats.map((category, i) => {
return (
<TouchableOpacity key={category.id} style={
(i === cats.length - 1) ?
{...styles.categoryItem, ...styles.categoryItemLast} :
styles.categoryItem
} onPress={()=>{this.onPressCategory(category)}}>
<View style={{
...styles.categoryLabel, ...{
backgroundColor: category.color
}
}}>
<Icon name={category.icon} size={25} style={styles.categoryIcon}
color={category.iconColor}/>
</View>
<Text>{category.title}</Text>
</TouchableOpacity>
)
})}
</ScrollView>
);
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
}
componentWillMount() {
const categories = this.props.categories;
this.setState({
categories: categories
});
}
onPressCategory(category) {
this.props.setCategoryInfo(category);
this.props.navigation.navigate('CategoryInfoScreen', {});
}
}
function mapStateToProps(state) {
return {
categories: state.categories
}
}
function mapDispatchToProps(dispatch) {
return {
setCategoryInfo: (category) => dispatch({ type: 'SET_CATEGORY_INFO', data: category })
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoriesListScreen)
CategoryEditScreen.js
import { connect } from 'react-redux';
class CategoryEditScreen extends React.Component {
static navigationOptions = ({navigation}) => {
return {
title: 'Edit Category',
headerRight: <Button onPress={() => {navigation.getParam('categoryChangedDispatch')()}} title="Save"/>
}
};
render() {
const category = this.state.category;
...
}
componentWillMount() {
const category = this.props.categoryInfo;
this.setState({
category: category
});
}
componentDidMount() {
this.props.navigation.setParams({
categoryChangedDispatch: this.categoryChangedDispatch.bind(this)
});
}
categoryChangedDispatch() {
let cats = this.props.categories;
cats.push({
id: 3,
title: 'My third category',
color: '#7495e7',
icon: 'airplane',
iconColor: '#2980B9'
});
this.props.categoryChanged(cats);
this.props.navigation.navigate('CategoryInfoScreen', {});
}
}
function mapStateToProps(state) {
return {
categories: state.categories,
categoryInfo: state.categoryInfo
}
}
function mapDispatchToProps(dispatch) {
return {
categoryChanged: (categories) => dispatch({ type: 'CATEGORIES_CHANGED', data: categories }),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoryEditScreen)
It seems it's related to the fact you update your local state (this.state.categories) based on the categories property, only during the componentWillMount phase but not when props are updated (which should happen after you dispatched the new data).
Try using this.props.categories directly within your CategoriesListScreen component.
before
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'CATEGORIES_CHANGED':
return {
...state,
categories: action.data
};
default:
return state;
}
};
after
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'CATEGORIES_CHANGED':
return {
...state,
categories: Object.assign([], action.data)
};
default:
return state;
}
};
Reducer has problem. I made the absolutely new array. It works! But I think it isn't normal :)