Redux-thunk dispatch not working - reactjs

I am looking at thunk and trying to figure out how to implement an api call. It is not working so I have gone back to the very basics. When I click on the button it shows 'Getting here! in the console, but nothing is showing when I console.log(dispatch). Am I missing something here?
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { connect, Provider } from 'react-redux';
import thunk from 'redux-thunk'
import axios from 'axis';
const store = createStore(
reducer,
applyMiddleware(thunk)
);
function fetchUser() {
return axios.get('https://randomuser.me/api/');
}
function addUser() {
console.log('Getting here');
return (dispatch) => {
console.log(dispatch) //not showing anything
return fetchUser().then(function(data){
console.log(data);
});
};
}
class App extends React.Component {
addUser() {
addUser();
}
render() {
return (
<button onClick={this.addUser.bind(this)}>+</button>
)
}
}
const mapPropsToState = function(store){
return {
newState: store
}
}
var ConnectApp = connect(mapPropsToState)(App);
ReactDOM.render(
<Provider store={store}>
<ConnectApp />
</Provider>,
document.getElementById('app')
)

You cannot call addUser() from your component as a regular function. You have to use a mapDispatchToProps function and pass it to your connect call in order to be able to dispatch addUser().
const mapDispatchToProps = dispatch => ({addUser: () => dispatch(addUser())})
then
ConnectApp = connect(mapPropsToState, mapDispatchToProps)(App);
Now you can call it as a prop.
addUser() {
this.props.addUser();
}

You're not actually dispatching the thunk. You're calling it directly. You need to pass the inner () => {} thunk function to dispatch.
There's several ways to handle this. Since you're not providing a mapDispatchToProps argument to connect, the App component will automatically be given this.props.dispatch(). So, in App.addUser(), you could do this.props.dispatch(addUser()).
Another way would be to pre-bind the addUser action creator. You could do this with the syntax var ConnectApp = connect(mapPropsToState, {addUser})(App). Then, when you call this.props.addUser(), it will automatically dispatch the result.
I have some discussion on use of action creators and binding at http://blog.isquaredsoftware.com/2016/10/idiomatic-redux-why-use-action-creators/ , and a couple gists with sample code for binding and dispatching at https://gist.github.com/markerikson/6c7608eee5d2421966d3df5edbb8f05c and https://gist.github.com/markerikson/f46688603e3842af0f9720dea05b1a9e .

If you do not want to use connect function then you can use useDispatch() hook.
const dispatch = useDispatch();
useEffect(() => {
dispatch(addUser())
}, [])
You can use componentDidMount or UseEffect to fetch data.

Related

adding 'dispatch' to a redux action breaks action (w/out dispatch the action runs)

I am using redux with redux-thunk middleware. The function in question makes a GET request to an API and upon response (.then()) dispatches the res to my redux store via an action.
For some reason when I pass dispatch to the parent function the function never runs. When I remove dispatch the parent function does run...(???) I have multiple other components within the same app that follow this exact same pattern successfully. For some reason this particular component is behaving in this strange way although i've triple checked and the boilerplate is all the same.
Here is my store.jsx:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from '../reducers/root_reducer'
const configureStore = (preloadedState = {}) =>
createStore(
rootReducer,
preloadedState,
applyMiddleware(thunk, logger)
);
export default configureStore;
my actions my_team_actions.js:
import * as APIUtil from '../util/api/my_team_api_util';
export const RECEIVE_ORG_SURVEY = "RECEIVE_ORG_SURVEY"
export const receiveOrgSurvey = survey => ({
type: RECEIVE_ORG_SURVEY,
survey
});
export const getOrganizationSurvey = () => dispatch => {
debugger
APIUtil.getOrgSurvey()
.then((res) => {
debugger
dispatch(receiveOrgSurvey(res))
})
.catch(err => console.log(err))
}
my API call my_team_api_util.js:
import axios from 'axios';
export const getOrgSurvey = () => {
return axios.get(`/api/mongo/organizations/test`)
}
component container my_team_container.jsx:
import { connect } from 'react-redux';
import MyTeam from './my_team';
import { getOrganizationSurvey } from '../../actions/my_team_actions';
const mSTP = state => {
return {
user: state.session.user,
};
};
const mDTP = dispatch => {
return {
getSurvey: () => getOrganizationSurvey(),
};
};
export default connect(mSTP, mDTP)(MyTeam);
component my_team.jsx:
import React from 'react';
class MyTeam extends React.Component {
constructor(props) {
super(props)
this.createTeam = this.createTeam.bind(this);
}
createTeam() {
this.props.getSurvey();
}
render() {
return (
<div className="my-team-frame frame">
<div className="my-team-container">
<div className="contact-data-container">
<div className="contact-data-header header">Contact a Data Scientist</div>
</div>
<div className="myteam" onClick={this.createTeam}>BUTTON</div>
</div>
</div>
)
}
}
export default MyTeam;
On the client side the my_team component renders fine and when I click the button which calls the function which will eventually dispatch my action it only seems to run when dispatch is NOT included in getOrganizationSurvey() in my_team_actions.js i.e. I hit both debuggers (and the second one with a correct res object). When dispatch is included (as shown in the snippet above) I don't hit either debuggers nor are any errors thrown.
I'm really scratching my head on this, any input is appreciated!
Thanks,
Ara
God I am a moron... XD
I said I triple checked... I should have checked 4 times! The issue was in my components container my_team_container.jsx I simply forgot to pass dispatch in the map dispatch to props object!
I fixed it by adding dispatch to the getSurvey callback...
my_team_container.jsx
import { connect } from 'react-redux';
import MyTeam from './my_team';
import { getOrganizationSurvey } from '../../actions/my_team_actions';
const mSTP = state => {
return {
user: state.session.user,
};
};
const mDTP = dispatch => {
return {
getSurvey: () => dispatch(getOrganizationSurvey()),
};
};
export default connect(mSTP, mDTP)(MyTeam);
it's funny how you can spend 2 hours on a problem, think it's hopeless and then as soon as you ask for help take another look at it and the solution just stares right back at you 😂

Calling action creator inside the function. Error:Actions may not have an undefined "type" property?

I know action creator should have a type of property only then it would be able to dispatch. Since I am having a function call which ultimately leads to one action creator which have type property then Why it is showing me this problem.
When I tried to directly dispatch start game action creator it works but since I have to implement some more function inside them so I needed then inside the function.
How to implement the same?
Menu.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {startGame} from '../actions';
import {loadMenu} from '../actions';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
const page_Banner={
marginTop:'35px',
fontSize:'45px',
textAlign:'center',
letterSpacing:'20px',
fontWeight:'bold'
};
const spacebar_screen={
marginTop:'35px',
color:'grey'
}
class Menu extends Component {
componentDidMount() {
this.props.dispatch(loadMenu());
console.log(this.props.dispatch);
console.log(this.props.isPlaying);
}
render() {
return (
<div style={page_Banner}>
Redux Tetris
{!this.props.isPlaying?<h2 style={spacebar_screen}>Press spacebar to start the game</h2>:null}
</div>
)
}
}
Menu.propTypes={
isPlaying:PropTypes.bool,
}
// function mapDispatchToProps(dispatch){
// return bindActionCreators({loading:loadMenu},dispatch);
// }
const mapStateToProps = (state) => ({
isPlaying: state.gameStatus.currentState !== 'IDLE',
});
export default connect(mapStateToProps)(Menu);
Action.js
import constants from "../gameConstants/constants";
export const startGame=()=>{
const ShapeMapping=constants;
const current_Shapeno=Math.floor(Math.random()*7);
const next_Shapeno=Math.floor(Math.random()*7);
const current_Shape=ShapeMapping[current_Shapeno];
const next_Shape=ShapeMapping[next_Shapeno];
return {
type:"START_GAME",
current_Shape,
next_Shape
};
}
export const pauseGame = () => ({
type: "PAUSE_GAME",
});
export const unpauseGame = () => ({
type: "UNPAUSE_GAME",
});
export const gameOver = () => ({
type: "GAME_OVER",
});
export const loadMenu=()=>({
function(dispatch,getState){
function handleSpacebar(event){
if(event.keyCode==32){
dispatch(loadGame());
window.removeEventListener('keyup',handleSpacebar);
console.log('here')
}
}
window.addEventListener('keyup',handleSpacebar);
}
})
export const loadGame=()=>({
function (dispatch,getState){
dispatch(startGame());
}
})
The issue is in loadMenu and loadGame action creators. You're returning an object with an anonymous function which doesn't make any sense. An action creator is supposed to return an object with a type and the minimal data to define the action and return a function if you're using redux-thunk.
Keep the actions creators clean like you've done in gameOver and handle everything else in reducers or using the redux pub/sub pattern.
See this answer by Dan Abramov https://github.com/reduxjs/redux/issues/787

Actions must be plain object. Use custom middleware

I am using Redux,redux-thunk with react. I am returning an object but still getting the error.
authActions.js
export function register(){
return (dispatch)=>{
console.log("in register action");
dispatch({type:'auth_user'})
}
}
calling this action from Register.js using connect and props
import * as actions from '../actions/authActions';
class RegisterForm extends React.Component{
handleRegister = (e)=>{
e.preventDefault();
console.log("inside handle register");
console.log(this.props);
this.props.register();
}
}
var Register = connect(mapStateToProps,actions)(RegisterForm);
Error is
Actions must be plain objects. Use custom middleware for async actions.
EDIT 1
Implemented redux-thunk like below.
import thunk from 'redux-thunk';
const store = createStore(authReducer,applyMiddleware(
thunk,
loggerMiddleware
),window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
The code can be found on github using link
https://github.com/abhikulshrestha22/social-network/tree/master/client
You're using register from mapDispatchToProps:
this.props.register();
But it's just:
var Register = connect(mapStateToProps,actions)(RegisterForm);
So, calling register wouldn't work because it's in actions, actions.register:
var Register = connect(mapStateToProps,{register: actions.register})(RegisterForm);
Now, it should fix your issue.
actions/index.js
// with thunk
export const register = () => dispatch => (
dispatch({ type: 'auth_user' })
)
// without thunk
export const register = () => ({ type: 'auth_user' })
component/example.js
import React from 'react';
import { connect } from 'react-redux';
import { register } from '../actions';
const Example = ({ register }) => (
<button onClick={register}>Register</button>
)
export default connect(null, { register })(Example);
reducers/index.js
const authReducer = (state=false, {type, payload}) => {
switch(type) {
case 'auth_user': return !state;
default: return state;
}
}
There is nothing wrong with the current setup that you showed above.
I think your mapDispatchToProps may be the root cause of this problem.
You should declare your connect likes this
export default connect(
null,
{ register }
)(Register);
instead of (if I'm not wrong)
const mapDispatchToProps = dispatch => {
return {
register: () => {
dispatch(register());
}
};
};
export default connect(
null,
mapDispatchToProps
)(Register);
That's my guess. Hope this may help you.
handleRegister = (e) => {
...
this.props.dispatch(register());
}
Of course:
Apply redux-thunk middleware
In Register.js import register() action
Connect Register component to Redux store with react-redux connect()
EDIT:
If this simplified code (without mapDispatchToProps) doesn't work, something is wrong with your question.
Maybe your action payload contains something that's not a plain object? E.g. promise returned by axios?
Code Sandbox according to your question, everything seems to work fine:
https://codesandbox.io/s/j2mny6rvnv

Possible to dispatch an action without connect?

export default class App extends Component {
...
componentDidMount() {
//registering event listener
BackgroundGeolocation.on('location', this.onLocation, this.onError);
}
onLocation(location) {
//wishing to dispatch an action to update variable in store
}
render() {
return (
<Provider store={store}>
<AlertProvider>
<MainNavigator screenProps={{ isConnected: this.state.isConnected }} />
</AlertProvider>
</Provider>
);
}
}
From what I understand, I can't possibly connect to store in this component as we are just configuring and pass the store into Provider. How would i possibly dispatch an action in my onLocation event?
You can directly dispatch using the store object.
store.dispatch({type:"UPDATE_VARIABLE", payload:variable.value})
If you are in some other component where the store object isnt readily available, export it from the main component and import it to a location where it is needed and use the above statement
react-redux has own hooks to call them
const dispatch = useDispatch();
const state = useSelector((state) => state);
do not forget to import them
import { useDispatch, useSelector } from 'react-redux';
vs code will autocomplete if you write useDispatch
You can use this code and add the constructor in the class like this
constructor(props) {
super(props);
}
onLocation() {
this.props.dispatch({type: GET_THINGS_REQUESTED, payload: value});
}
UmiJS 3.x users can do:
import { getDvaApp } from 'umi';
const dispatch = getDvaApp()._store.dispatch;
dispatch({ … })

Integrating Dispatch Actions in Container Component Pattern

So I'm completely confused on how to integrate the Container and Component Pattern. I've been reviewing examples all morning and nothing seems to be clicking. How I have been worked with React previously on my first project was fetch the data within my view components and then pass that data down as props using the #connect which works, but in an "automagically" way to me at this time.
import React;
...
import {action} from 'path/to/action.js';
#connect((store) => {return{ key: store.property}});
export class Component{
componentWillMount(){
this.props.dispatch(action());
}
}
As I'm working more with React I want to learn the more "correct" way of building out with Redux and understand on a deeper level what is happening.
What I have setup is
index.jsx (This renders all of my HOCs)
|
App.jsx (Container)
|
Auth.jsx (Component)
|
Layout.jsx (Component) - Contains app content
--or--
AuthError.jsx (Component) - 401 unauthenticated error page
Authentication is handled through an outside resource so this app will not control anything with Logging in or out. There will be no log in/out states simply receiving an object from an API that identifies the User Role & Authenticated Boolean.
What I would like to happen is when the App loads, it will fetch data from a mock API, JSON Server. From there it will render the Auth component. The Auth component will take in props from App.jsx and either render the Layout.jsx or AuthError.jsx.
Where I'm running into issues is how this should be integrated. I'm going to omit lines of code I don't think absolutely pertain to the question.
store.js
import { applyMiddleware, combineReducers, createStore } from 'redux';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import promise from 'redux-promise-middleware';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from './reducers';
const middleware = applyMiddleware(promise(), thunk, createLogger());
export default createStore(reducer, composeWithDevTools(middleware));
index.jsx
import React from 'react';
import store from './store.js';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App.jsx';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { authenticateUser } from '../actions/authActions.js';
import Auth from '../components/Auth.jsx';
class App extends Component {
constructor(props) {
super(props);
this.state = {
authenticated: false // this needs to be set
};
}
componentWillMount() {
console.log('APP PROPS', this.props);
// this.props.actions.authenticateUser();
authenticateUser(); // this runs but doesn't run the dispatch function
// What I think needs to happen here Dispatch an Action and then setState referring back to how I would previous build with React Redux.
}
render() {
return (
<Auth app_name={ApplicationName} authenticated={this.state.authenticated} {...this.props} />
);
}
}
const mapStateToProps = state => {
console.log('redux store auth state', state);
return {
auth: state.auth
};
};
const mapDispatchToProps = dispatch => {
return { actions: bindActionCreators(authenticateUser, dispatch) };
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Auth.jsx
import React from 'react';
import { Route } from 'react-router-dom';
import AuthError from './AuthError.jsx';
import Layout from './Layout.jsx';
export default function Auth(props) {
console.log('AUTH PROPS', props);
const renderLayout = () => {
if (props.authenticated == true) {
return <Layout app_name={props.app_name} />;
} else {
return <AuthError />;
}
};
return <Route path="/" render={renderLayout} />;
}
authReducer.js
export default function reducer(
state = {
authenticated: null
},
action
) {
switch (action.type) {
case 'AUTH_SUCCESSFUL': {
return {
...state,
authenticated: action.payload.authenticated
};
break;
}
case 'AUTH_REJECTED': {
return {
...state,
authenticated: false
};
}
}
return state;
}
authActions.js
import axios from 'axios';
export function authenticateUser() {
console.log('authenticate user action has been called');
return function(dispatch) {
// nothing runs within this block so it's leading me to believe nothing is being `dispatch`ed
console.log('dispatch', dispatch);
axios
.get('localhost:3004/auth')
.then(response => {
dispatch({ type: 'AUTH_SUCCESSFUL', payload: response.data });
console.log('response', response);
})
.catch(err => {
dispatch({ type: 'AUTH_REJECTED', payload: err });
console.log('error', err);
});
};
}
Right now inside of App.jsx I can console the state of the authReducer and I can call authenticateUser() in my actions. But when I call authenticateUser() the return dispatch function doesn't run. Should I be dispatching the auth action in App.jsx? Or should I be dispatching the auth in Auth.jsx as a prop to then have App.jsx fetch the data? Just a bit lost on breaking this apart and what piece should be doing what work.
I'll do a brief explanation about it to help you to understand those patterns and don't get in confusion anymore (I hope).
So, let's forget reducers for a moment to focus on container, action creator and component pattern.
Component
A lot of people implement components by wrong way when using it with redux application.
A better component approach for redux is, implement it with stateless pattern (see Functional Components). Let's see in practice:
// components/Subscribe.js
import React from 'react'
import PropTypes from 'prop-types'
const Subscribe = ({text, confirmSubscription}) =>
<div>
<p>{text}</p>
<button onClick={confirmSubscription}>Confirm</button>
</div>
Subscribe.propTypes = {
subtitle: PropTypes.string.isRequired
}
Subscribe.defaultProps = {
subtitle: ''
}
export default Subtitle
This allows you to optimize component footprint because they have less features than stateful components (or class components), so you will win some performance and keep focused on component objective.
Container
In other hand, Container is a kind of component with some logical implementation. Container is a pattern created to bind React and Redux, because both should't interact directly. This means, a Container render the component, handle some component events (for example, form onSubmit) and feed components with application state. So, the Container is the best place to interact with Redux. (react-redux)[https://github.com/reactjs/react-redux] and Redux make this task a bit easier. So a simple Container to feed and capture interactions on Subscribe component could be like this:
// containers/SubscribeContainer.js
import React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { confirmSubscription } from 'actions/subscription'
import Subscribe from 'components/Subscribe'
const mapStateToProps = state => ({
text: state.subscription.text
})
const mapDispatchToProps = dispatch =>
bindActionCreators({
confirmSubscription
}, dispatch)
const Container = connect(mapStateToProps, mapDispatchToProps)
export default Container(Subscribe)
Action Creator
An action creator (or action creators), is just a collection of or a function where return an action. Simple like that:
// actions/subscription
export const CONFIRM_SUBSCRIPTION = 'actions.confirmSubscription'
export function confirmSubscription() {
return {
type: CONFIRM_SUBSCRIPTION
}
}
For now, we have the triad pattern, Component, Container and Action Creator implemented, from here, you just need two more things to make this working with Redux.
Create a subscription store.
Handle CONFIRM_SUBSCRIPTION (in case to update app's state)
Return a new state
The magic will happen when you return a new state from any reducer, the mapStateToProps will be called and you will receive the new state as argument and from there, React will update your components when necessary, in case of those components are stateless, PureComponent (works only with single level states and props) or custom shouldComponentUpdate.
Another thing to keep on mind is to not do fetch or async execution inside Components, Containers and Action Creators, instead, you can use middleware like redux-thunk to compose a custom middeware to capture actions and handle that before be sent to reducers.
your authenticateUser returns a function, you need to literally run the function. The right way to do that is to add a property in your mapDispatchToProps
const mapDispatchToProps = dispatch => {
return { authenticateUser: () => dispatch(authenticateUser()) };
};
Then, in your componentWillMount function, call
this.props.authenticateUer()
Check this

Resources