Navigate to new component after dispatch from mapDispatchToProps - react-native

After setting the language for my app by clicking the English or French button, I need to navigate to the next screen. How do I call this.props.navigation.navigate('HomeScreen') directly after a dispatch?
I tried putting both in a function and calling it from onPress, but that didn't work.
LanguageScreen.js
<Button
title="English"
onPress={this.props.english}
/>
<Button
title="Français"
onPress={this.props.french}
/>
LanguageContainer.js
import React, { Component } from 'react';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import LanguageScreen from '../screens/LanguageScreen.js';
const mapStateToProps = (state, props) => ({
language: state.language
})
const mapDispatchToProps = (dispatch) => ({
french: () => {
dispatch({ type: 'FRENCH' });
},
english: () => {
dispatch({ type: 'ENGLISH' });
},
})
export default connect(mapStateToProps, mapDispatchToProps)(LanguageScreen)
store.js
import { createStore } from 'redux'
export const language = (state = 'english', action) => {
switch (action.type) {
case 'FRENCH':
return 'french';
case 'ENGLISH':
return 'english';
default:
return state;
}
}
let store = createStore(language);
export default store;

mapDispatchToProps accepts ownProps as second argument so assuming your component receives navigate prop you can try something like this:
const mapDispatchToProps = (dispatch, ownProps) => ({
french: () => {
dispatch({ type: 'FRENCH' });
ownProps.navigation.navigate('HomeScreen');
},
english: () => {
dispatch({ type: 'ENGLISH' });
ownProps.navigation.navigate('HomeScreen');
},
}

Related

Why data is not loading from this dispatch action?

I am trying to learn redux.
I watch some tutorials and follow along with them. These tutorials are with class component.
So I try to change these into functional component.
Since I am just learning and not trying to make a big project I put actions, reducers and types into 1 file.
This is that file
import axios from 'axios';
export const FETCH_NEWS = 'FETCH_NEWS';
// Reducer
const initialState = {
newsList: [],
};
export const articlesReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_NEWS:
return {...state, newsList: action.payload};
default:
return state;
}
};
export const fetchNews = () => (dispatch) => {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((res) => {
dispatch({
type: FETCH_NEWS,
payload: res.data,
});
})
.catch((err) => {
console.log(err);
});
};
So I am using fetchNews props in News component
News component is like this
import { fetchNews }from '../../ducks/modules/Articles'
useEffect(() => {
fetchNews();
console.log('##############################')
console.log(newsList)
console.log('##############################')
},[])
const News = ({navigation, newsList, fetchNews}) => {
return (<View> .... </View>)
}
News.propTypes = {
fetchNews: PropTypes.func.isRequired,
newsList: PropTypes.array.isRequired
}
const mapStateToProps = state => {
return {
newsList: state.articlesReducer.newsList
}
}
export default connect(mapStateToProps, { fetchNews })(News);
As you can see I am console.logging in the useEffect hooks , I am console logging because no data are being loaded in the device
Here is a picture of empty array when component is mounted
My store component is like this
const reducer = combineReducers({
articlesReducer
});
const store = createStore(reducer, applyMiddleware(thunk,logger));
You are not dispatching the action correctly. I have added simpler way to use redux with function based components. You don't need to use connect.
export const fetchNews = () => (dispatch) => {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((res) => {
dispatch({
type: FETCH_NEWS,
payload: res.data,
});
})
.catch((err) => {
console.log(err);
});
};
export const selectNewsList = (state) => state.newsList; // this is known as a selector.
And your view will be:
import { useSelector, useDispatch } from 'react-redux';
import { fetchNews, selectNewsList }from '../../ducks/modules/Articles'
const News = () => {
const newsList = useSelector(selectNewsList);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchNews());
},[])
console.log(newsList); // This will print empty array first, but will print again as data is populated.
return (<View> .... </View>)
}

Api call happens only the first time in react-native redux

I am new to using react native redux and I am facing an issue that the api call is made only once, what if i click on another button which should render a different response based on the params and display it on the component which is a flatlist in my case. Please have a look at my code.
RecordListAction:
import { FETCH_RECORD_LIST, FETCH_RECORD_SUCCESS, FETCH_RECORD_FAILURE } from './types.js'
export const fetchRecordList = () => ({
type: FETCH_RECORD_LIST
})
export const fetchRecordSuccess = json => ({
type: FETCH_RECORD_SUCCESS,
payload: json
})
export const fetchRecordFailure = error => ({
type: FETCH_RECORD_FAILURE,
payload: error
})
export const fetchRecordListApi = () => {
console.log("Now I'm here!")
return async dispatch => {
dispatch(fetchRecordList());
let response = await
fetch(url, {
method: 'POST',
headers: {
'tenantid': '1',
'Content-Type': 'application/json',
'language': '1',
'userid': '11'
},
body: JSON.stringify(global.recordListBody)
}).then((response) => response.json())
.then((responseJson) => {
console.log("RecordList Action Value" + responseJson)
dispatch(fetchRecordSuccess(responseJson.records));
}).catch(error => {
dispatch(fetchRecordFailure(error))
}) }}
recordListReducer.js:
import {FETCH_RECORD_REQUEST,FETCH_RECORD_SUCCESS,FETCH_RECORD_FAILURE}
from "../actions/types"
const initialState = {
isFetching: false,
errorMessage : '',
record :[]
};
const recordListReducer = (state = initialState,action) => {
switch(action.type){
case FETCH_RECORD_REQUEST:
return { ...state, isFetching: true }
case FETCH_RECORD_FAILURE:
return { ...state, isFetching: false, errorMessage: action.payload };
case FETCH_RECORD_SUCCESS:
return{...state, isFetching:false, record:action.payload}
default:
return state
}};
export default recordListReducer;
RecordListContainer.js
import React, { Component } from 'react'
import { Text, View, StyleSheet, ActivityIndicator, Button } from 'react-native'
import PropTypes from 'prop-types';
import {fetchRecordListApi} from "../redux/actions/recordListAction"
import {connect} from "react-redux";
import DetailsViewMode from '../Enums/DetailsViewMode'
import RecordList from '../Components/RecordListComponents/RecordList';
import { Icon, Divider } from 'react-native-elements';
class RecordListContainer extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.props.dispatch(fetchRecordListApi());
}
render(){
let content = <RecordList record = {this.props.recordList.record}/>
if(this.props.recordList.isFetching){
content= <ActivityIndicator size="large" />
}
}}
RecordListContainer.propTypes = {
fetchRecordListApi : PropTypes.func.isRequired,
recordList : PropTypes.object.isRequired}
const mapStateToProps = state =>{
return{
recordList: state.posts
};
}
export default connect(mapStateToProps)(RecordListContainer);
rootReducer.js :
import recordListReducer from './recordListReducers';'
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
posts : recordListReducer,
});
export default rootReducer;
You could make recordListBody part of redux state or react context. Or you could make recordListBody observable and respond to changes. Here is an example of making recordListBody observable:
//object combined with global.recordListBody to add listeners
// and notify them of changes
const recordListBodyObserver = ((observers) => {
const removeObserver = (observer) => () => {
observers = observers.filter((o) => o !== observer);
};
return {
notify: (value) =>
observers.forEach((observer) => observer(value)),
add: (observer) => {
observers.push(observer);
return removeObserver(observer);
},
};
})([]);
let recordListBodyValue;
//your global object with recordListBody that will notify
// listeners if a value for recordListBody is set
const global = {
set recordListBody(value) {
//notify all listeners;
recordListBodyObserver.notify(value);
//set the new value
return (recordListBodyValue = value);
},
get recordListBody() {
return recordListBodyValue;
},
};
//function to create increasing id
const id = ((id) => () => id++)(1);
class App extends React.PureComponent {
componentDidMount() {
this.removeListener = recordListBodyObserver.add(
(value) => {
//you can dispatch your action here using value
// do not use global.recordListBody here becasue
// that has the old valuee
console.log(
'recordListBody changed from:',
global.recordListBody,
'to value:',
value
);
}
);
}
componentWillUnmount() {
//clean up listener when component unmounts
this.removeListener();
}
render() {
return (
<button
onClick={() => (global.recordListBody = id())}
>
Change recordListBody
</button>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am using componentDidUpdate and check if props value is changed, the api is again called when the body coming in props is changed.

action not dispatching react native, state is coming out fine

I am unable to dispatch an action to reducer.
State comes out just fine. Not sure where I have gone wrong.
For other user reducer, dispatch works just fine. my project has nested drawer navigation > tabs navigation > stack navigation and current page is the 3rd stack screen. Not sure if that is the issue or what.
Store.js
import { combineReducers, createStore } from '#reduxjs/toolkit';
import cartReducer from './cartSlice';
import userReducer from './userSlice';
const rootReducer = combineReducers({
userReducer: userReducer,
cartReducer: cartReducer,
})
const configureStore = () => createStore(rootReducer)
export default configureStore
cartSlice.js
const initialState = {
cart: [{ key: 1, data: { freq: 'Daily', duration: 30 } }]
}
const cartReducer = (state = initialState, action) => {
switch (action.types) {
case "ADD_TO_CART":
console.log(action)
return {
...state,
cart: [...state.cart, { key: 2, data: action.data }]
}
case "REMOVE_FROM_CART":
const idx = state.cart.map((cartItem) => (
cartItem.key === action.id
))
const tempNewCart = [...state.cart]
if (idx >= 0) {
tempNewCart.splice(idx, 1)
}
return { ...state, cart: tempNewCart }
case "CLEAR_CART":
return {
...state,
cart: []
}
default:
return state
}
}
export default cartReducer
SubscriptionQuantity Component
const mapStateToProps = (state) => {
console.log(state)
return {
cart: state.cartReducer.cart
}
}
const mapDispatchToProps = (dispatch) => {
// console.log(dispatch)
return {
addtoCartStore: (freq, duration) => dispatch({
type: 'ADD_TO_CART',
data: {
freq: freq,
duration: duration
}
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SubscriptionQuantity)
function call for dispatch
const addtoCart = () => {
addtoCartStore('Alternate', 30)
navigation.navigate("CartScreen")
}
The issue was with the switch statement!!!!
it should be switch(action.type) not action.types. that's why it was going in on the default route. changed the typo and works fine now.!

How to configure redux-thunk properly?

I've been trying to implement a ChatApp in order to learn React Native. I'm trying to use redux-thunk in order to sign users up in firebase.
The problem I'm running into is that everyone seems to do things slightly different in their examples/explanations. Little confused. Can anyone explain what I'm doing wrong?
// Reducer
import * as types from './actionTypes'
const initialState = {
restoring: false,
loading: false,
user: null,
error: null,
}
const session = (state = initialState, action) => {
switch(action.type) {
case types.SESSION_RESTORING:
return { ...state, restoring: true }
case types.SESSION_LOADING:
return { ...state, restoring: false, loading: true, error: null }
case types.SESSION_SUCCESS:
return { restoring: false, loading: false, user: action.user, error: null }
case types.SESSION_ERROR:
return { restoring: false, loading: false, user: null, error: action.error }
case types.SESSION_LOGOUT:
return initialState
default:
return state
}
}
export default session
// Actions
import * as types from './actionTypes'
import firebaseService from '../../services/firebase'
export const signupUser = (email, password) => {
return (dispatch) => {
dispatch(sessionLoading())
firebaseService.auth()
.createUserWithEmailAndPassword(email, password)
.catch(
error => {
dispatch(sessionError(error.message))
}
)
let unsubscribe = firebaseService.auth()
.onAuthStateChanged(
user => {
if (user) {
dispatch(sessionSuccess(user))
unsubscribe()
}
}
)
}
}
//Actions
const sessionSuccess = (user) => ({
type: types.SESSION_SUCCESS,
user
})
const sessionLoading = () => {
type: types.SESSION_LOADING
}
const sessionError = (error) => {
type: types.SESSION_ERROR,
error
}
// Configure store
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import reducer from './session'
const configureStore = () => {
// eslint-disable-next-line no-underscore-dangle
return createStore(reducer, compose(applyMiddleware(thunk)))
}
export default configureStore
// Create store
import React from 'react'
import { Provider } from 'react-redux'
import configureStore from './store'
import Screen from './screens/Authorization'
const store = configureStore()
const App = () =>
<Provider store={store}>
<Screen />
</Provider>
export default App
// MapDispatchToProps
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import SignupComponent from './Component'
import { signupUser } from '../../../store/session/actions'
const SignupContainer = (props) =>
<SignupComponent signupUser={props.signup}/>
SignupContainer.propTypes = {
signup: PropTypes.func.isRequired
}
const mapDispatchToProps = {
signup: signupUser
}
export default connect(null, mapDispatchToProps)(SignupContainer)
The error I get is:
Actions must be plain objects. Use custom middleware for async actions.
You need to wrap your object in parens if you want to use arrow functions like that:
const sessionLoading = () => ({
type: types.SESSION_LOADING
})
const sessionError = (error) => ({
type: types.SESSION_ERROR,
error
})

react-native with redux error

I follow the demo to create a project. I used react-native with redux, then I get this error "undefined is not a function(evaluating '(0, _redux.createStore)(_todoListRedux.reducer)')`".
This is my code:
index.android.js
import {AppRegistry, View} from 'react-native'
import {createStore} from 'redux'
import {reducer} from './todoListRedux'
const store = createStore(reducer)
import App from './App'
const AppWithStore = () => <App store={store} />
AppRegistry.registerComponent('redux', () => AppWithStore)
todoListRedux.js
export const types = {
ADD: 'ADD',
REMOVE: 'REMOVE',
}
export const actionCreators = {
add: (item) => {
return {type: types.ADD, payload: item}
},
remove: (index) => {
return {type: types.REMOVE, payload: index}
}
}
const initialState = {
todos: ['Click to remove', 'Learn React Native','Write Code','Ship App'],
}
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
}
any help will be appreciated!
The store added in createStore. Should be a store with combineReducers.
And as far as I know, the middleware should be passed as second parameter in the createStore.
Something like.
import { applyMiddleware, createStore } from 'redux';
import rootReducer from '../store';
const middleware = applyMiddleware({you can pass here (logger or thunk, or the available options)});
//example
const middleware = applyMiddleware(thunk);
const store = createStore(rootReducer, middleware);