I have several actions, that are need to be used with another one action,
more specific i have actions to work with doument and one action to save it, and i dont wont to repeat calling of save document, so i tried to create myself wrapper on the mapDispatchToProps and thunk.
I need to each action I pass to the method performed to thunk actions -
From
function changeAction() {
return: {
type: types.CHANGE
}
}
To
function change() {
return(dispatch, getState) => {
dispatch(changeAction())
saveDocument(getState());
}
}
I tried to create method to map actions to thunk
export function mapUpdatePresentationsToProps(actions) {
const mappedActions = {};
Object.keys(actions).map(action => {
mappedActions[action] = function() {
return (dispatch, getState) => {
dispatch(action)
dispatch(updatePresentation(getState()))
}
}
})
return mappedActions;
}
And I always get an error 'Actions must be plain objects. Use custom middleware for async actions.' Am i doing something wrong? I dont know how i can debug them. Will be grateful for any help.
I guess you need to change:
dispatch(action)
to something like:
dispatch(actions[action]())
Right now it seems that you try to dispatch a string
Related
In my react project I have an actions file that looks like this:
const startLoad = () => async (dispatch) => {
dispatch({
type: CONTENT_LOAD,
});
};
// Get all content
export const getContents = () => async (dispatch) => {
dispatch(startLoad());
// some more code...
};
So in this example, I know that dispatch is coming from the middleware and getContents has access to it because it was mapped using mapDispatchToProps. So I am assuming when getContents is called, its really called like this -> dispatch(getContents) but can this be replicated in plain JavaScript so I can see what is really going on? If I am wrong about why getContents has access to dispatch please let me know.
For example, how isstartLoad able to use dispatch just because dispatch called startLoad? I have noticed that it will also work if I call it like this: dispatch(startLoad(dispatch));.
Passing dispatch in to startLoad actually makes more sense to me so I don't get why that isn't required.
Edit: this is the closest example I could come up with on my own.
const fun = () => (fun2) => {
fun2();
}
const fun2 = () => {
console.log('hello');
}
fun()(fun2);
So I am assuming when getContents is called, its really called like
this -> dispatch(getContents) but can this be replicated in plain
JavaScript so I can see what is really going on? If I am wrong about
why getContents has access to dispatch please let me know.
redux-thunk has quite simple implementation
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
As you can see it checks if action is a function.
And bonus, if you like you can get the whole state and extra argument in params following dispatch
For example, how isstartLoad able to use dispatch just because
dispatch called startLoad? I have noticed that it will also work if I
call it like this: dispatch(startLoad(dispatch));.
It looks like middleware also handles above case.
I have seen problems like this here in Stackover flow but none of them seem to solve my problem. I am trying to dispatch an action using redux thunk but unfortunately, my thunk function is not firing the return statement.
I have tried using the redux thunk like I usually do:
export function handleLikeThunk(id, likeType){
console.log('thunk1')
return (dispatch) => {
console.log('thunk2', id);
}
}
However the code only reaches the 'thunk1' message. It never displays 'thunk2'
I am trying to handle a like button so first I imported the redux thunk function:
import { handleLikeThunk } from '../redux/actions/posts';
I have a function that calls the thunk whenever the like button is pressed:
handleLike = (e) => {
const { likes, id } = this.state
if(e.target.getAttribute('name') === 'upVote'){
this.setState((perviouState)=>({
likes: perviouState.likes + 1
}));
handleLikeThunk(id, 'upVote');
}
if(e.target.getAttribute('name') === 'downVote' && likes > 0){
this.setState((perviouState)=>({
likes: perviouState.likes - 1
}));
handleLikeThunk(id, 'downVote');
}
}
And in order to do that, I connect my component to the store
const mapDispatchToProps = (dispatch) => {
return {
handleLikeThunk: (id, likeType) => dispatch(handleLikeThunk(id,
likeType))
}
}
export default connect(null, mapDispatchToProps)(Post)
Like I said, I want to be able to use the thunk function to post this info in the back-end but it never goes inside the return statament inside the thunk function.
Looks like you're calling the raw function and not the one that you put in your mapDispatchToProps, try changing handleLikeThunk(id, 'upVote'); to this.props.handleLikeThunk(id, 'upVote');
The situation is I am creating a single board which will hold a collection of note cards (each note has an id, title and body), and each note card will have a button to delete it. Also the application will be syncing with firebase, so my main question is how to pass arguments to middlewares AND do it inside of mapDispatchToProps. The following is my code to point out where my success with middleware and where I am currently blocked.
To hydrate the app on startup, I dispatch a middleware function that gets the data from firebase, and then dispatches actions handled by reducers and finally gets updated by the container/presentation component.
Middleware function:
export function hydrateApp(dispatch) {
dispatch({type: 'PENDING'});
fireBaseDBRef.once('value').then(snapshot => {
let firebaseNotes = snapshot.val()
let notes = [];
// populate notes using firebaseNotes, nothing exciting
dispatch({ type: 'DONE', notes: notes });
// the 'DONE' action.type is handled by the reducer and passes data
// to the container component successfully
}).catch(e => {
dispatch({type: 'ERROR', error: e});
});
}
Container component:
const mapStateToProps = state => {
return {
notes: state.boardReducer.notes
};
};
const mapDispatchToProps = dispatch => {
return {
addNote: () => {
dispatch(boardMiddleware.createNote);
}
};
};
const BoardContainer = connect(
mapStateToProps,
mapDispatchToProps
)(BoardPresentation);
So far so good, and this is what I added to the same middleware and container component files to handle delete scenarios.
Middleware function:
export function deleteNote(id) {
return (dispatch) => {
dispatch({type: 'PENDING'});
//firebase stuff happening here
dispatch((type: 'DONE'});
}
}
Container component:
const mapDispatchToProps = dispatch => {
return {
addNote: () => {
dispatch(boardMiddleware.createNote);
},
removeNote: (id) => {
dispatch(boardMiddleware.deleteNote(id));
}
};
};
The problem is that deleteNote gets called non-stop on startup, I don't even need to click the button.
I know the code presented may not make a whole bunch of sense, but the crux of my problem is that I need to some how pass an id to the middleware function when the user clicks on the button, and because I'm passing the function as a prop, it for some reasons decides to just call it a million times.
I could call boardMiddleware.deleteNote function inside the presentation component just like the examples in the official redux page do, but I'm wondering if there is a way of doing it the way I'm trying to do.
I also thought about binding the argument into the middleware function, but that also doesn't feel right, something like this
removeNote: (id) => {
dispatch(boardMiddleware.deleteNote.bind(id));
}
Thanks for any help in advance!
I'm getting a bit confused with getState() in redux. I am using the thunk middleware.
I have an auth action which is an async action. But I have an action which runs before which checks if a token exists in state and if its still valid.
My problem is i can't seem to check the state when I have called the action. Thought I could just use getState but that doesn't seem to be a function.
container.js
componentDidMount() {
this.props.authCheck()
}
...
function mapDispatchToProps(dispatch) {
return {
authCheck: () => checkApiStatus()(dispatch)
}
}
Action.js
export const checkApiStatus = () => (dispatch, getState) => {
const expires_at = getState().api.expires_at
if (!expires_at || isDateGreater(expires_at)) {
// dispatch async action
dispatch(apiAuth())
}
return
}
Anyone have any ideas. Or perhaps better way of implementing something like this?
Thanks
The problem is you explicitly calling the returned function in you mapDispatchToProps method and passing only one argument. Instead call dispatch(checkApiStatus()) then redux-thunk will take care of passing the right arguments to the returned method. Should look like this
function mapDispatchToProps(dispatch) {
return {
authCheck: () => dispatch(checkApiStatus())
}
}
The title may not clear enough, please consider this example:
If I have a data table, which you can select multiple rows, and click action button like delete.
now in my actions.js:
(selectedRows is an array that contains the row indexes, getSelectedPostIds is a selector which will fetch and convert selectedRows to postIds)
import { getSelectedPostIds } from 'selectors'
export const deletePosts = () => (dispatch, getState) => {
// encapsulate the parameter `postIds` in action
const postIds = getSelectedPostIds(getState())
dispatch({ type: 'DELETE' })
deletePostsApi(postIds)
// .then(...)
// .catch(...)
}
is there any problem in this design? Or I should avoid using getState in an action and just pass postIds as a parameter to the action:
export const deletePosts = postIds => dispatch => {
dispatch({ type: 'DELETE' })
deletePostsApi(postIds)
// .then(...)
// .catch(...)
}
The only difference is that who should fetch the state (use the selector) from store, 1. action or 2. the component who will dispatch the action (via mapStateToProps).
I'm not sure about the approach 1, and the approach 2 will make my component contains a lot of props just because some actions need them (or maybe this is totally fine?).
thanks.
This might be a matter of taste. I usually like to access getState directly since, as you point out, avoids passing a lot of props. And by doing that the action is easier to integrate in different components (I just need to call it instead of additionally editing the mapStateToProps). Also, since in the end both ways are accessing the global store, the intended redux data flow is not compromised in any way.
You can use redux-thunk if you want to work with state in your action creators. :)
https://github.com/gaearon/redux-thunk
function yourActionCreator() {
// Redux-thunk will catch all action creators that return functions
return (dispatch, getState) => {
// u can use state here
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
// Dispatch your action creator as you would normally do
dispatch(increment());
};
}