I just started using redux, and I'm trying to fetch some data from Firebase and put it in my store. I did some research into the matter and it looked like to me that this:
export const addData = (database) => {
return dispatch => database.ref.once('value').then((snapshot) => {
dispatch({
type: 'STORE_DATA',
payload: snapshot.val()
});
}, (err) => {
dispatch(
{type: 'STORE_FAILED'})
})
}
should work, however I am getting an error "Error: Actions must be plain objects. Use custom middleware for async actions" when I call
store.dispatch(addData(firebase.database()))
I'm not sure what I'm doing wrong.
You need to use a custom middleware like redux thunk or sagas in order to create an action as a function instead of a plain object.
npm i redux-thunk
Then import this into the file where your store is located.
import thunk from 'redux-thunk'
And add it as an argument to your applyMiddleware function and applyMiddleware as an argument to your createStore function.
const store = createStore(rootReducer,
applyMiddleware(
thunk
)
);
This should give you the ability to run your async function inside your action.
Related
I got this error:
"Actions must be plain objects. Use custom middleware for async actions."
even though i use thunk as a middleWare.
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import { allReducers } from './reducers';
import thunk from 'redux-thunk';
import { getAllCourses } from './../../utils/courseServices';
export const store = createStore(allReducers, composeWithDevTools(
applyMiddleware(thunk),
// other store enhancers if any
));
//initiliaze
store.dispatch(getAllCourses())
//subscribe
store.subscribe(()=>console.log(store.getState()))
and also my action:
import { getAllCourses } from "../../utils/courseServices"
export const courseAction = () =>{
return async dispatch =>{
//fetching data from server
const {data} = await getAllCourses()
await dispatch({type:"INIT" , payload: data.courses})
}
}
You are dispatching getAllCourses there, not courseAction. That's probably your problem.
Also, please be aware that in new code you should be using configureStore of #reduxjs/toolkit, not createStore. Modern Redux does not use switch..case reducers, ACTION_TYPES, immutable reducer update logic, hand-written action creators or connect/mapStateToProps. (And that is nothing new, but the recommendation since 2019).
You are probably following an outdated tutorial - please follow the official Redux tutorial
I am using redux in a phaser and react based game, dispatching actions in React Components, Utils, and Axios API Calls. I know there are useDispatch hook and connect can be used for React Components, however, based on my own experience, I can use dispatch from store directly as well.
import store from 'my-redux/store';
const { dispatch } = store;
dispatch(actionCreator(1));
I use the above way to dispatch actions in Phaser Scene and Utils to change states such as health and score, and dispatch actions in Axios async API calls. It is convenient and light compare to using useDispatch or connect, so why is it not recommended to dispatch from store except for isomorphic app?
Reference for why not use dispatch from store for isomorphic apps:
redux issue!916
redux issue!184
redux issue!362
I even want to bind dispatch to action creators like this:
import store from 'my-redux/store';
const { dispatch } = store;
export const handleSetSomeThing = (state) => {
const action = {
type: ActionTypes.SOME_TYPE,
payload: state,
};
dispatch(action);
};
Then I can call the above function like this without importing store or using connect, redux hook:
handleSetSomeThing({ health: 5 })
Any idea?
I am quite new to React and Redux framework. What I am trying to do is reading a propery say zipCode from an API call on componentDidMount. And now I have to save it to redux store.
I am really confused reducers and actions etc. And not sure how and where to configure those.
Help much appreciated.
Redux can seem confusing at first glance, as I have experienced. But like everything, you will start to understand it quickly, especially if you practise making your own redux application(with the support of this community) and succeed by trial and error.
What you must understand about Redux are the following (I wont go into detail, ill keep it simple and basic, as well as relate it to your question):
1. Actions: These are as the name suggests, actions fired by the view/component. E.g. when you request to fetch the zipcode, this will be dispatched to the action.
componentDidMount(){
this.props.fetchZipCode(85001);
}
action.js
export const fetchZipCode= (zip) => ({
type: 'FETCH_ZIPCODE',
zip,
});
2. Reducers: These handle the actions request. As described in Redux docs 'The reducer is a pure function that takes the previous state and an action, and returns the next state'
reducer.js
const ZipcodeInfo = (state = 0, action) => {
switch (action.type) {
case 'REQUEST_ZIPCODE':
return {
...state
};
case 'RECEIVE_ZIPCODE':
return {
...state
zip: action.data.zipcode
};
case 'RECEIVE_ZIPCODE_FAILED':
return {
...state
}
default:
return state;
}
};
3. Store: This is what brings everything together. It holds application state, allows access to the store.
index.js (note: this is the root file of the application, so all states can be passed from the store to all components in your app)
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux'
import ZipcodeInfo from './reducers'
const store = createStore(ZipcodeInfo)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
4. Middleware: Now the middleware is not necessary, but if you are making async calls you will need to use a middleware to pass information to the API etc... There a many middlewars to use for redux.
Redux-saga middleware example
import {call, put, takeEvery, fork} from 'redux-saga/effects';
import * as service from '../Services/api';
import * as actions from '../actions/zipcode';
//The worker: will handle the action and terminate
function* fetchZipcode(action){
try{
yield put(actions.requestZipcode());
const [zipcode] = yield [call(service.getZipcodeAPI, action.zip)];
yield put(actions.receiveZipcode(zipcode));
} catch(e){
yield put(actions.receiveZipcodeFailed(e));
}
}
//The watcher: will watch for dispatched actions and fork a worker on every action
function* watchfetchZipcode(){
yield takeEvery('FETCH_ZIPCODE', fetchZipcode);
}
export default function* zipcodeSaga(){
yield fork(watchfetchZipcode);
}
I personally prefer Redux-Saga as a middleware, but it may be confusing as it uses generators(from es6) and more unknown terms.
Watch the below tutorial. It's a simple example to understand.
https://www.youtube.com/watch?v=OSSpVLpuVWA
Im absolutely a beginner in React Native, Redux, and friends.
Now I try to create an app with React Native and Redux.
In the process I found this error :
I try to use Redux in my app like this :
export const confirmRegister = (userdata) => {
return (dispatch) => {
return(
fetch(URI + path, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'x-application-id' : AppId,
'hashing' : this.generateHashing(userdata)
},
body: userdata,
})
.then((response) => {
return response.json()
})
.then((response)=>{
dispatch(setDataAfterRegister(response.result))
})
.catch(error => {alert(error)})
)
};
};
export function setDataAfterRegister(data){
return{
type:'REGISTERED',
name:data.name,
token:data.access_token
}
}
I'm still learning how to use Redux.
Any helps will be really appreciated.
Here's my store:
import { createStore } from 'redux';
import rootReducer from './reducers';
let store = createStore(rootReducer);
export default store;
confirmRegister function returns a function:
export const confirmRegister = (userdata) => {
return (dispatch) => {
Redux don't know how to handle it natively. You need a middleware to do that. Most popular middleware that will let you return a function from your action creators is a redux-thunk.
First install it:
npm install --save redux-thunk
and then set up middleware:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
Your Action creator confirmRegister returns a function, But in React action creator returns a action that is a plain JS Object.
So you need some middleware to tell react about this asynchronous call.
You can use Redux-thunk , redux-Sagas and other middlewares.
This will let your action creator return a function and that function on getting response will dispatch a new action object.
You can install the middlerware using npm or yarn
npm install --save redux-thunk
yarn add redux-thunk
Add that to your store config file.
And for that Warning:Cannot update during an existing state transition
you have to setState of a component either in ComponentWillReceiveProps or ComponentWillMount. You are probably setting the state in render method.
`
I am new to React with Redux structure and its concept. In my application, I need to navigate specific path in an action after login. My action code is:
const login = (resp:Object) => (dispatch: any) =>{
// api call
if(apiCall is success){
window.location.href = "http://localhost:3000"+localStorage.getItem("pathBeforeLogin");
}
else{
window.location.href = "http://localhost:3000/login";
}
});
}
This code is working fine but my senior asked me to do this work without using window.location.href. As We are using the react-router v4, browserHistory.push is also not working.
react-router-redux provides a Redux middleware that allow you to navigate via Redux actions.
From https://github.com/reactjs/react-router-redux:
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { routerMiddleware, push } from 'react-router-redux';
// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory);
const store = createStore(
reducers,
applyMiddleware(middleware)
);
// Dispatch from anywhere like normal.
store.dispatch(push('/foo'));
So in your example, instead of assigning strings to window.location.href, you'd do the following:
store.dispatch(push('/login'));