Error: Actions must be plain objects. Use custom middleware for async actions. (React Native) - react-native

I am trying to make it so that if an item called code is not set in state with redux, it is called from AsyncStorage and state is set.
import {AsyncStorage} from 'react-native'
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux'
import {handlePhoneNumber, saveCode} from './../../actions/RegistrationActions';
class EnterPhoneNumberScreen extends React.Component {
componentDidMount() {
let code = this.props.registration.code;
console.log("code is", code);
if(code){
// Do nothing
}else{
console.log("in the else");
this.props.getAndSetCode();
}
}
}
const getAndSetCode = () => dispatch => {
console.log("in get and set Code");
AsyncStorage.getItem('code')
.then((data) => {
console.log("data is ", data);
dispatch(saveCode(data));
console.log("in getAndSetCode method, Code is ", data);
})
}
const mapDispatchToProps = dispatch => (
bindActionCreators({
handlePhoneNumber,
getAndSetCode: () => dispatch(getAndSetCode()),
}, dispatch)
);
export default connect(mapStateToProps, mapDispatchToProps)(EnterPhoneNumberScreen);
The console outputs the following:
LOG code is null
LOG in the else
LOG in get and set Code
LOG data is 3tgvgq
LOG in getAndSetCode method, Code is 3tgvgq
I know thunk is properly installed because it is running elsewhere in the application. saveCode is just a normal action:
export const saveCode = code => ({
type: "SAVE_CODE",
payload: code
})
And this error appears in the iphone11 simulator:
How do I fix this?

Related

Whats the correct way to invoke a registered task?

I am using expo version 41.0.0 and i defined task and export the registered task. This is how
the code looks like
import * as BackgroundFetch from "expo-background-fetch";
import * as TaskManager from "expo-task-manager";
import axios from 'axios';
const TASK_NAME = "BACKGROUND_TASK";
TaskManager.defineTask(TASK_NAME, () => {
try {
// fetch data here...
const receivedNewData = "Simulated fetch " + Math.random()
console.log("My task ", receivedNewData)
return receivedNewData
? BackgroundFetch.Result.NewData
: BackgroundFetch.Result.NoData
} catch (err) {
return BackgroundFetch.Result.Failed
}
});
export const RegisterBackgroundTask = async () => {
try {
await BackgroundFetch.registerTaskAsync(TASK_NAME, {
minimumInterval: 5, // seconds,
});
console.log("Task registered")
} catch (err) {
console.log("Task Register failed:", err)
}
}
and this is how I invoke the registered task
import { RegisterBackgroundTask } from '../../helper/background-task';
const SomeComponent = ({ ...props }) => {
RegisterBackgroundTask();
...rest of the code
return (
<View>
other stuff here...
</View>
)
}
PS: I run the app through Expo and I am using IOS
Is this the correct way to invoke registered task, or can it be called inside a lifecycle method like useEffect ?

react-redux useSelector() hook not working

I am new to React Native Programming. So, please tell me in detail. thank you.
calling use Selector
I am calling use Selector inside my functional component like this:
import { useDispatch, useSelector } from 'react-redux';
const AddAddressScreen = ({ navigation }) => {
const dispatch = useDispatch();
const data = useSelector(state => state);
console.log(data + "happy Coding");
return (
<View style={styles.container}>
<View>
);
}
export default AddAddressScreen;
My reducer looks like this
case types.API_LOGIN_SUCCESS:
if (action.result.result.mobile_verified === false) {
return {
...state,
onLoad: false,
result: action.result,
status: action.status,
error: null,
navigation: action.navigation.navigate("VerifyMNO")
};
} else {
return {
...state,
onLoad: false,
result: action.result,
status: action.status,
error: null,
navigation: action.navigation.navigate("AddAddress")
};
}
here my mobile number is verified so I move to the address screen.
where I use Use Selector which gives me an error. while I remove above two lines my code runs successfully.
My saga looks like this
export function* watchLoginUserInfo() {
yield takeLatest(types.LOGIN_USER, loginApiSaga)
}
My root saga
import { all, fork } from 'redux-saga/effects';
import { watchLoginUserInfo, } from './authenticationSagas';
function* rootSaga() {
yield all([
watchLoginUserInfo(),
])
}
export default rootSaga;
My Store looks like this
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../redux/reducers/root-reducer.js'
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../redux/sagas/rootSaga';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
export {store};
when ever I use use Selector hook in my code it gives me the following error.
error 1
error 2, 3, 4
Use the select effect from redux-saga inside of a reducer: https://redux-saga.js.org/docs/api/#selectselector-args
For example const selectedState = yield select(state => state);.
The useSelector hook is for use inside of a function component.
EDIT: since the above doesn't seem to be the issue, I think the issue is that you're calling navigation functions from within your reducer. Reducer code can have no side effects, so you can't call navigation.navigate(...) from within the reducer. This will need to happen in the saga code instead. It might be able to be done in the loginApiSaga or in a dedicated saga that is triggered by API_LOGIN_SUCCESS.

Calling LoadingIndicator as required

I would like to call the LoadingIndicator or a busy indicator during a process so that the user cannot navigate away while the process is in progress.
I cannot find anything in the documentation on how to do this.
In react-admin, the loading indicator reacts to custom Redux actions. If you want to start it, you can dispatch them manyally:
import { useDispatch } from 'react-redux';
import { fetchStart, fetchEnd } from 'react-admin';
const MyComponent = () => {
const dispatch = useDispatch();
const startLoader = () => {
dispatch(fetchStart());
}
const endLoader = () => {
dispatch(fetchEnd());
}
return (/* ...*/);
}
However, this doesn't block user navigation. If you want to block users, you should use a material-ui Dialog.

React Redux reducer not returning

I have an application that has to return only future events. I am able to parse through my firebase realtime db and return only future events in my action. It then passes this array of data to my reducer. In my reducer, I am logging the payload. The console log displays the data I want. Now in my component screen, I console log to see what data I should see from the reducer and it is empty.
My action:
export const getAllEvents = () => {
var currentTime = Date.now();
return (dispatch) => {
var ref = firebase.database().ref('Events/');
ref.orderByChild("availableSpots").on("value", function(snapshot) {
snapshot.forEach(child => {
var time = child.val().epochTime;
if (currentTime < time) {
console.log("Events redux: ", child.val());
dispatch({ type: GET_EVENT_LIST, payload: child.val() });
}
})
});
});
}
}
As you can see I am console logging the child.val() which comes out as expected below:
Now I have this hooked up to my reducer via dispatch.
import { GET_EVENT_LIST } from '../actions/types';
const INITIAL_STATE = {
eventList: [],
};
const eventListReducer = (state = INITIAL_STATE, action) => {
switch (action.type){
case GET_EVENT_LIST:
console.log("action count", action.payload);
return { ...state, eventList: [...state.eventList, action.payload] };
default:
console.log("default ");
return state;
}
};
export default eventListReducer;
As you can see the console log of the action.payload is being logged and produces an expected result:
Now back in my component screen:
import { getUserThunk, getAllEvents } from '../actions';
import {connect} from 'react-redux';
class Home extends Component {
componentWillMount() {
console.log("Component will mount");
this.props.getUserThunk();
this.props.getAllEvents();
}
render() {
console.log("usersadf ", this.props.userReducer)
console.log("Props in home ", this.props.eventListReducer)
return (
//some return code here
);
}
export default connect(
state=>({userReducer: state.userReducer, eventListReducer: state.eventListReducer}),
{ getUserThunk, getAllEvents }
)(Home);
Now as you can see I do have different redux action and reducer hooked up and it comes through. However, the one in question returns empty.

How to update state in reducer

I have a problem with my reducer. I am using redux to create a login page. I have successfully logged in yet I failed to navigate to the next page. The state in my reducer is not updated. How do I solve this?
This is how I wrote my reducer:
import { LOGIN_SUCCESS } from '../actions/types';
const INITIAL_STATE={
isLoginSuccess:false,
}
export default function (state=INITIAL_STATE, action){
switch(action.type){
case LOGIN_SUCCESS:
return {
isLoginSuccess : true
}
default:
return INITIAL_STATE;
}
}
This is how I wrote my action:
import axios from 'axios';
import * as helper from '../common';
import { LOGIN_SUCCESS } from './types';
export const attemptLogin = (username, password) => async dispatch => {
let param = {
txtNomatrik: username,
txtPwd: password,
public_key: helper.PUBLIC_KEY,
secret_key: helper.SECRET_KEY
}
console.log(`${helper.ROOT_API_URL}/v1/basic/ad/std/login`)
let login_res = await
axios.post(`${helper.ROOT_API_URL}/v1/basic/ad/std/login`, param)
console.log(login_res.data);
if (login_res.data.status == 'Successful Login') {
const { login } = login_res.data;
await AsyncStorage.seItem('Login_token', username);
await AsyncStorage.setItem('profile', JSON.stringify(login));
dispatch({ type: LOGIN_SUCCESS, payload: { isLoginSuccess : true } });
}
}
I want to use the isLoginSuccess in my index file to navigate login to the next page like this:
componentWillReceiveProps(nextProps){
if(nextProps.isLoginSuccess){
this.props.navigation.navigate('logged');
}
}
Below is how I connect redux:
const mapStateToProps = ({ auth }) => {
return {
isLoginSuccess: auth.isLoginSuccess
}
}
export default connect(mapStateToProps, actions)(LoginScreen);
Below is my combineReducer file:
import {combineReducers} from 'redux';
import news from './welcome_reducer';
import auth from './login_reducer';
export default combineReducers({
news,
auth
})
How do I solve this? I feel lost now, have tried a lot of ways to solve it . The Api call for login is successful but the action is not dispatched at all. I can't update the state and I can't navigate to the next page. Please help me