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
Related
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 😂
I have an enpoint that takes formData and API gets POST method only. When the page renders it takes the initial values but I have another form to change the parameters, once I change the params it does not send the updated values.
PS. Using Redux Create Slice here is my code;
Form Page
parametersChange = form => {
... };
formatData = data => {
return form;
};
And my createSlice Method;
export const functionName = (data = {}) => (dispatch, getState) => {
........
}));
};
this.props.getLossAvoidanceList({...data }); };
This should update my createSlice Method, however, the datain my createSlice method is always returns undefined. Where am I doing wrong ?
const mapStateToProps = (state) => ({
.....
});
const mapDispatchToProps = {
...
};
export default connect(
....
)(withRouter(LossAvoidance));
I show your code and nothing found any wrong but I think the problem is in declaration of HOC
try that way
import { compose } from "redux";
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(LossAvoidance);
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
I have a problem with dispatching a action from componentDidMount...
error is : TypeError: this.props.postDetails is not a function
Action.js
export const postDetails = data => ({
type: "POST_DETAILS",
post: data
})
Container/GetDetails.js
import Details from '../components/Details'
import { postDetails } from '../actions'
const mapStateToProps = state => ({ post: state.post });
const mapDispatchToProps = dispatch => bindActionCreators({postDetails}, dispatch);
const GetDetails = connect(
mapStateToProps,
mapDispatchToProps
)(Details)
export default GetDetails
Component/Details.js
import React from 'react'
import { postDetails } from '../actions'
class Details extends React.Component {
constructor(props){
super(props);
}
componentDidMount() {
console.log("did mount details");
this.props.postDetails();
}
render() {
return (
<div>
Details page
</div>
)
}
}
export default Details;
Can someone help me? Why i have this error?
In App.js (or wherever you are importing the Details component), are you using the path to your GetDetails container (not component)?
I moved state from a component to a container and forgot to update the import path which gave me this same error. Updating the import path to the container took care of it.
Edit:
For example, I have an apiLanding folder that has apiLanding.js (the component) and apiLanding-container.js (the container).
In my app.js, I needed to change
import apiLanding from './components/apiLanding/apiLanding';
to
import apiLanding from './components/apiLanding/apiLanding-container';
That way, the app now has access to the redux state and actions. This was a silly mistake and may not be your issue, but wanted to share just in case the import path was overlooked.
You have to return an object, where the keys are your props. See docs.
const mapDispatchToProps = dispatch => ({ postDetails: bindActionCreators({postDetails}, dispatch) })
Or, you can use the shorthand notation:
const GetDetails = connect(
mapStateToProps,
{ postDetails }
)(Details)
I don't see bindActionCreator imported. Use eslint to get rid of these errors
There are two things which don't really seem right to me. Personally I never used bindActionCreators. I would just create my mapDispatchToProps as following:
const mapDispatchToProps = dispatch => {
return {
postDetails: () => dispatch(actions.postDetails)
};
};
But also your postDetails call expects an argument, which you should add in your function call. So your mapDispatchToProps would look like this:
const mapDispatchToProps = dispatch => {
return {
postDetails: (data) => dispatch(actions.postDetails(data))
};
};
Also you're importing your action as postDetails. Are you sure that this is just one action? And not a combination of all actions in your file? Note how I added your function as actions.postDetails instead of just postDetails.
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.