How to configure redux-thunk properly? - react-native

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
})

Related

How to Get createAsyncThunk In The Correct Way Data React Native

I am a new in react native Redux Newest Documentation. I want to use createAsyncThunk
to get my api data using Axios.
Below my code :
App.js
import React, { useState } from 'react';
import { Provider } from 'react-redux';
import { configureStore } from '#reduxjs/toolkit';
import ApiChartingSlice from './redux/slice/api/ApiChartingSlice';
import LoginNavigator from './navigation/LoginNavigator';
const store = configureStore({
reducer: {
ApiChartingMenu: ApiChartingSlice
}
});
export default function App() {
return (
<Provider store={store}>
<LoginNavigator />
</Provider>
);
}
ApiChartingAxios.js
import axios from "axios";
import { BasicAuthUsername, BasicAuthPassword } from "../utility/utility";
export default axios.create({
baseURL: 'https://blablabla.id/index.php',
headers: {
auth: {
username: BasicAuthUsername,
password: BasicAuthPassword
}
}
});
SubApiChartingAxios.js
import ApiChartingAxios from "../ApiChartingAxios";
export const SubApiChartingMenu = async () => {
const response = await ApiChartingAxios.get('/ApiChartingMenu',{
params: null
});
return response;
};
ApiChartingSlice.js
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import { SubApiChartingMenu } from '../../../api/subapi/SubApiChartingAxios';
export const ApiChartingMenuThunk = createAsyncThunk(
'ApiChartingMenu',
async () => {
const response = await SubApiChartingMenu();
console.log(response);
return response.data.Data;
}
)
// status: 'idle' | 'loading' | 'succeeded' | 'failed',
const ApiChartingMenuSlice = createSlice({
name: 'ApiChartingMenu',
initialState: {
apiData: {},
status: 'idle',
error: null
},
reducers: {},
extraReducers: {
[ApiChartingMenuThunk.pending.type]: (state, action) => {
state.playerList = {
status: "loading",
apiData: {},
error: {},
};
},
[ApiChartingMenuThunk.fulfilled.type]: (state, action) => {
state.playerList = {
status: "idle",
apiData: action.payload,
error: {},
};
},
[ApiChartingMenuThunk.rejected.type]: (state, action) => {
state.playerList = {
status: "idle",
apiData: {},
error: action.payload,
};
},
}
});
export default ApiChartingMenuSlice.reducer;
And the last is my screen output:
ChartScreen.js
import { useNavigation } from '#react-navigation/native';
import React, { useEffect, useState, useCallback } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, KeyboardAvoidingView, TextInput, Button } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { ApiChartingMenuSlice, ApiChartingMenuThunk } from '../../redux/slice/api/ApiChartingSlice';
const ChartScreen = () => {
console.log('ChartScreen');
const dispatch = useDispatch();
console.log(dispatch(ApiChartingMenuThunk()));
const chartData = useSelector(state => state.ApiChartingMenu.apiData);
console.log(chartData);
return (
<View>
<Button title="test" onPress={() => {}} />
<ChartComponent />
</View>
);
};
export default ChartScreen;
Problem:
I don't know why in my ChartScreen.js this line : console.log(dispatch(ApiChartingMenuThunk()));
return :
Promise {
"_U": 0,
"_V": 0,
"_W": null,
"_X": null,
"abort": [Function abort],
"arg": undefined,
"requestId": "oqhkA7eyL_VV_ea4FDxr3",
"unwrap": [Function unwrap],
}
But in ApiChartingSlice.js in this line console.log(response);
return the correct value.
So, what is the correct way to retrive the value from the createAsyncThunk from my ChartScreen.js
The Api content is a list menu.
I want when first open the apps It execute the redux and show all my list menu.
But now just try to console.log the ApiChartingMenuThunk in ApiChartingSlice.js is not working.
Can anybody solve and guide me to a solution ? Thank You
I figured it out myself the problem is on this file :
ApiChartingSlice.js
and this line should be :
[ApiChartingMenuThunk.fulfilled.type]: (state, action) => {
state.playerList = {
status: "idle",
apiData: state.apiData.push(action.payload),
error: {},
};
also you need to dispatch using this code :
in file ChartScreen.js
this is how we dispatch it.
const toggleGetMenuHandler = useCallback(() => {
dispatch(ApiChartingMenuThunk())
.unwrap()
.then((originalPromiseResult) => {
// console.log(originalPromiseResult);
})
.catch((rejectedValueOrSerializedError) => {
console.log(rejectedValueOrSerializedError);
})
}, [dispatch]);
useEffect(() => {
toggleGetMenuHandler();
}, [toggleGetMenuHandler]);
},
Now this code : const chartData = useSelector(state => state.ApiChartingMenu.apiData);
will have a correct data.

React-native redux-saga error: takeLatest$1 requires a saga parameter

I created an App with some components and using the redux-saga in the following component:
// AlphaScreen.js
import ... // react & react-native
import { useSelector, useDispatch } from 'react-redux';
import { getUser } from '../redux/ducks/user';
const AlphScreen = props => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getUser());
}, [dispatch]);
const users = useSelector((state) => state.user.user);
console.log(users);
return (
<View><Text>Blah</Text></View>
);
}
// redux/ducks/user.js
export const SET_USER = "SET_USER";
export const GET_USER = "GET_USER";
export const setUser = (user) => ({
type: SET_USER,
user // user: user
});
export const getUser = () => ({
tye: GET_USER
});
const initialState = {
user: undefined
};
const userReducer = (state = initialState, action) => {
switch(action.type) {
case SET_USER:
const {user} = action;
return {...state, user:user};
default:
return state;
}
};
export default userReducer;
// redux/defaultStore.js
import {applyMiddleware, combineReducers, createStore} from 'redux'
import createSagaMiddleware from "redux-saga";
import counterReducer from './ducks/counter'
import userReducer from './ducks/user';
import { watcherSaga } from './sagas/saga';
const reducer = combineReducers({
user: userReducer
});
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const store = createStore(reducer, applyMiddleware(...middlewares));
sagaMiddleware.run(watcherSaga)
export default store;
// redux/sagas/saga.js
import { takeLatest } from 'redux-saga/effects';
import { handleGetUsers } from './handlers/user';
import { GET_USER } from '../ducks/user';
export function* watcherSaga() {
yield takeLatest(GET_USER, handleGetUsers); //<- getting error takeLatest$1 requires a saga parameter
}
// redux/sagas/handlers/user.js
import { call, put } from "redux-saga/effects";
import { setUser } from "../../ducks/user";
import { requestGetUser } from "../requests/user";
export function* handleGetUser(action) {
try {
const response = yield call(requestGetUser);
const { data } = response;
yield put(setUser(data));
} catch (error) {
console.log(error);
}
}
// redux/sagas/request/user.js
import axios from 'axios'
const requestGetUser = () => {
return axios.request({
method: "get",
url:"https://jsonplaceholder.typicode.com/users"
});
}
export { requestGetUser };
But I have following error:
takeLatest$1 requires a saga parameter
at node_modules/#redux-saga/core/dist/io-1d6eccda.js:37:10 in check
at node_modules/#redux-saga/core/dist/redux-saga-effects.dev.cjs.js:386:2 in validateTakeEffect
at node_modules/#redux-saga/core/dist/redux-saga-effects.dev.cjs.js:402:22 in takeLatest$1
at src/redux/sagas/saga.js:6:10 in watcherSaga
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:1161:17 in next
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:1112:6 in proc
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:1371:19 in immediately$argument_0
at node_modules/#redux-saga/core/dist/redux-saga-core.dev.cjs.js:60:15 in immediately
at [native code]:null in runSaga
at src/redux/configureStore.js:16:0 in <global>
at App.js:7:0 in <global>
at node_modules/expo/AppEntry.js:3:0 in <global>
at http://192.168.1.154:19000/node_modules/expo/AppEntry.bundle?platform=android&dev=true&hot=false&minify=false:141908:3 in global code
The above error occurred in task watcherSaga
Any suggestions for fixing the error? Thanks
To me it looks like a typo, handleGetUsers vs handleGetUser :)

Fetching data with redux doesn't work for me, initialState don't change

I started my RN project without redux and when I fetch data (using formData) it works fine without redux, but I've decided to inculude Redux in my project. that's what I did:
I created two new folder , one for actions (constants and ations files) and the other for store witch contains a folder for reducer
after that I created 3 actions like this: getScoreList, getScoreListSuccess, getScoreListFailure
function getScoreListSuccess(data) {
return {
type: FETCHING_SCORE_LIST_SUCCESS,
data: data
}
}
in the same file I created function for fetching data using async await and formData:
import FormData from 'FormData';
const formData = new FormData()
formData.append('userId', '1');
formData.append('key', 'XXXXXXXXX');
export const getScoreFromAPI = () => {
return async dispatch => {
dispatch(getScoreList());
try {
let res = await fetch('https://www.***.com/***/***/***.php', {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
body: formData,
})
let json = await res.json();
dispatch(getScoreListSuccess(json));
} catch (err) {
console.log("the error is: " + error)
}
};
};
Then I set my reducer for 3 cases:
import {FETCHING_SCORE_LIST, FETCHING_SCORE_LIST_SUCCESS, FETCHING_SCORE_LIST_FAILURE} from '../../actions/constants'
const initialState = {
scoreList: [],
isFetching: false,
error: false,
}
export default function getScoreList(state = initialState, action) {
switch (action.type) {
case FETCHING_SCORE_LIST:
return {
...state,
isFetching: true,
scoreList: [],
};
case FETCHING_SCORE_LIST_SUCCESS:
return {
...state,
isFetching: false,
scoreList: action.data,
};
case FETCHING_SCORE_LIST_SUCCESS:
return {
...state,
isFetching: false,
error: true,
};
default:
return state;
}
};
and in another file (index.js) that's what I did:
import {combineReducers} from 'redux'
import getScoreList from './scoreListReducer'
const rootReducer = combineReducers({
getScoreListS: getScoreList,
})
export default rootReducer
then I created my store with thunk middleware in configStore.js file
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './Reducers/index'
export default function configureStore() {
let store = createStore(rootReducer, applyMiddleware(thunk))
return store
}
app.js file:
...
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './Store/Reducers/index'
const createStoreWithMW = applyMiddleware(thunk)(createStore);
const store = createStoreWithMW(rootReducer);
export default class App extends Component<Props> {
render() {
return (
<Provider store={store}>
<Navigation/>
</Provider>
);
}
}
And finally I connected my component with redux :
...
import { connect } from 'react-redux';
import {getScoreFromAPI} from '../../actions/actions';
import { bindActionCreators } from 'redux';
...
function mapStateToProps(state) {
return{
scoreListState: state.getScoreListS,
};
}
const mapDispatchToProps = dispatch => bindActionCreators({
fetchScoreList: getScoreFromAPI
}, dispatch)
export default connect(
mapStateToProps,
mapDispatchToProps
)(ScoreList);
to test it I console.warn scoreListState:
componentDidMount() {
console.warn(this.props.scoreListState);
}
this is what I have
I hope it was clear enough even if it's a little bit long

How to fix redux authentication in react-native-navigation?

I am creating redux authentication with react-native-navigation. It has two layouts which is switching by store auth state. When I am starting application in xCode simulator and trying to authenticate, it does not change a Auth layout to Main. But when I'm start Safari console to see messages from application, auth works fine and layout is changing. Please explain to me why this is happening?
index.js
import React, { Component, } from 'react';
import { Navigation, } from 'react-native-navigation';
import { Provider, } from 'react-redux';
import store from './src/store';
import registerScreens from './src/screens';
registerScreens(store, Provider);
export default class App {
constructor () {
this.auth = false;
store.subscribe(this.onStoreUpdate.bind(this));
this.start();
}
onStoreUpdate () {
const state = store.getState();
// if I'am write alert(state.auth) here, it show me false
if (this.auth != state.auth) {
this.auth = state.auth;
this.start();
}
}
start () {
switch (this.auth) {
case false:
Navigation.startTabBasedApp({
tabs: [{
screen: 'navigation.AuthScreen',
}, {
screen: 'navigation.RegisterScreen',
},],
});
break;
case true:
Navigation.startSingleScreenApp({
screen: {
screen: 'navigation.MainScreen',
},
});
break;
}
}
}
const application = new App();
auth.screen.js
import React, { PropTypes, Component, } from 'react';
import { ... } from 'react-native';
import { bindActionCreators, } from 'redux';
import { connect, } from 'react-redux';
import * as actions from './../actions';
class AuthScreen extends Component {
constructor (props) {
super(props);
this.handlePressEnter = this.handlePressEnter.bind(this);
}
handlePressEnter () {
// if I'am commenting line bellow and line in last then((), auth works fine
this.props.actions.request(true);
jsonFetch(url, {}).then((value) => {
return this.props.actions.auth(true);
}).catch((errors) => {
console.log(errors);
}).then(() => {
/* * */
this.props.actions.request();
});
}
render () {
....
}
}
function mapStateToProps (state, ownProps) {
return {
loading: state.loading,
};
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps) (AuthScreen);
actions/index.js
import * as constants from './../constants';
export const request = (state = false) => {
return {
type: constants.REQUEST,
payload: {
state,
},
};
}
export const auth = (state = false) => {
return {
type: constants.AUTH,
payload: {
state,
},
};
}
reducers/index.js
import { combineReducers, } from 'redux';
import * as constants from './../constants';
const loading = (state = false, action) => {
switch (action.type) {
case constants.REQUEST:
return action.payload.state;
default:
return state;
}
}
const auth = (state = false, action) => {
switch (action.type) {
case constants.AUTH:
return action.payload.state;
default:
return state;
}
}
const reducers = combineReducers({
loading,
auth,
});
export default reducers;
store.index.js
import { createStore, applyMiddleware, } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { createLogger, } from 'redux-logger';
import reducers from './../reducers';
const loggerMiddleware = createLogger();
const store = createStore(reducers, applyMiddleware(thunkMiddleware, loggerMiddleware));
export default store;

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);