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');
Related
I am using the redux action pattern (REQUEST, SUCCESS, FAILURE) along with redux saga. I made a watcher and worker saga just like that:
import axios from 'axios';
import { put, call, takeEvery } from 'redux-saga/effects';
import * as actionTypes from 'constants/actionTypes';
import * as actions from 'actions/candidates';
const { REQUEST } = actionTypes;
// each entity defines 3 creators { request, success, failure }
const { fetchCandidatesActionCreators, addCandidateActionCreators } = actions;
const getList = () => axios.get('/api/v1/candidate/');
// Watcher saga that spawns new tasks
function* watchRequestCandidates() {
yield takeEvery(actionTypes.CANDIDATES[REQUEST], fetchCandidatesAsync);
}
// Worker saga that performs the task
function* fetchCandidatesAsync() {
try {
const { data } = yield call(getList);
yield put(fetchCandidatesActionCreators.success(data.data));
} catch (error) {
yield put(fetchCandidatesActionCreators.failure(error));
}
}
const postCandidate = params => axios.post('/api/v1/candidate/', params).then(response => response.data).catch(error => { throw error.response || error.request || error; });
// Watcher saga that spawns new tasks
function* watchAddCandidate() {
yield takeEvery(actionTypes.ADD_CANDIDATE[REQUEST], AddCandidateAsync);
}
// Worker saga that performs the task
function* AddCandidateAsync({ payload }) {
try {
const result = yield call(postCandidate, payload);
yield put(addCandidateActionCreators.success(result.data));
} catch (error) {
yield put(addCandidateActionCreators.failure(error));
}
}
export default {
watchRequestCandidates,
fetchCandidatesAsync,
watchAddCandidate,
AddCandidateAsync,
};
My reducer has two flags: isLoading and success. Both flags change based on the request, success and failure actions.
The problem is that I want my component to render different things when the success action is put on the redux state. I want to warn the component every time a _success action happens!
The flags that I have work well on the first time, but then I want them to reset when the component mounts or a user clicks a button because my component is a form, and I want the user to post many forms to the server.
What is the best practice for that?
The only thing I could think of was to create a _RESET action that would be called when the user clicks the button to fill up other form and when the component mounts, but I don't know if this is a good practice.
You need to assign a higher order component, also called a Container, that connects the store with your component. When usgin a selector, your component will automatically update if that part of the state changes and passes that part of the state as a prop to your component. (as defined in dspatchstateToProps)
Down below i have a Exmaple component that select status from the redux state, and passes it as prop for Exmaple.
in example i can render different div elements with text based on the status shown in my store.
Good luck!
import { connect } from 'react-redux'
const ExampleComponent = ({ status }) => {
return (
<div>
{status === 'SUCCESS' ? (<div>yaay</div>) : (<div>oh no...</div>)}
</div>
)
}
const mapStateToProps = state => {
return {
status: state.status
}
}
const mapDispatchToProps = dispatch => {
return {}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ExampleComponent)
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())
}
}
I have an action which in turn must affect many other areas of my app state. In this case, when the user selects a website from a dropdown list, it must update many other components. I'm currently doing it like so:
setSelectedWebsite (websiteId) {
// The core reason for this component
this.props.setSelectedWebsite(websiteId);
// Fetch all associated accounts
this.props.fetchAllAccounts(websiteId)
// Several other "side effect" calls here...
}
In this interest of making one component serve one purpose, this feels like a bad practice.
What is the best practice for triggering multiple actions in one call from a component?
You could use redux-thunk.
Your component's method:
setSelectedWebsite(websiteId){
this.props.handleSetSelectedWebsite(websiteId) // this is a thunk
}
Your Redux file with action creators / thunks:
// this function is a thunk
export const handleSetSelectedWebsite = (websiteId) => (dispatch) => {
dispatch(setSelectedWebsite(websiteId))
dispatch(fetchAllAccounts(websiteId))
}
// these function are action creators (or could be other thunks if you style them the same way as the thunk above)
const setSelectedWebsite = (websiteId) => {
// your code
}
const fetchAllAccounts = (websiteId) => {
// your code
}
For handling complex side effects in a redux application, I would recommend looking at using Redux Sagas. I have seen its usage grow in popularity on projects large and small, and for good reason.
With sagas, in the example you have provided, you can emit a single action from a function provided through mapDispatchToProps and let a saga take care of the rest. For example: (following example assumes flux standard actions)
//import redux connect, react, etc
class SiteSelector extends React.Component {
render() {
const id = this.props.id;
return (
<button onClick={ () => this.props.action(id)>Click Me</button>
)
}
}
const mapStateToProps = (state) => ({
id: state.websiteId
})
const mapDispatchToProps = dispatch => ({
action: (id) => dispatch(setSelectedWebsite(id))
})
export connect(mapStateToProps, mapDispatchToProps)(SiteSelector)
Now you can handle the action emitted from setSelectedWebsite in a saga like so:
//import all saga dependencies, etc
export function* selectSite(action) {
const id = action.payload.id
yield put(Actions.selectWebsite(id))
const results = yield call(Api.fetchAllAccounts, id)
yield //do something complex with results
yield //do something else...
yield //and keep going...
}
// Our watcher Saga: spawn a new selectSite task every time the action from setSelectedWebsite is dispatched
export function* watchForSiteSelection() {
yield takeEvery('SITE_SELECTED_ACTION', selectSite)
}
For reference checkout the docs: Redux Sagas
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