Map redux dispatch to redux-form - reactjs

Sorry if this has been answered elsewhere, I'm a Redux beginner, and I couldn't find anything related.
I'm having trouble mapping dispatch to ReduxForm. Im setting up my reducers like this:
import { reducer as formReducer } from 'redux-form';
import foo from './foo';
import bar from './bar';
const rootReducer = combineReducers({
foo,
bar,
form: formReducer
});
export default rootReducer;
and then combining them like so:
import rootReducer from '../reducers/index';
const loggerMiddleware = createLogger();
const createStoreWithMiddlware = applyMiddleware(
thunkMiddleware,
loggerMiddleware
)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddlware(rootReducer, initialState);
return store;
}
and then mapping dispatch to props like this:
function mapStateToProps(state) {
return {
foo: state.foo,
bar: state.bar
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
And then I have a form component that I am wrapping in a reduxForm like this:
TestForm = reduxForm({
form: 'testForm',
fields: ['firstName', 'lastName', 'email']
})(TestForm);
export default TestForm;
But I get an error:
Warning: Failed propType: Required prop dispatch was not specified in ReduxForm(TestForm). Check the render method of TestFormParent.
What is the best way to inject dispatch into a form component?

Turns out the docs on npm are a little bit different, and more complete. If anyone else runs into this issue, use connectReduxForm instead of reduxForm solved the problem.

reduxForm works for version 4 of redux-form

Related

Notistack - withRouter and withSnackbar - How?

How do I combine the withSnackbar with withRouter?
const mapStateToProps = (state, props) => {
return {
loggedIn: state.loginReducer.loggedIn
};
};
export default withRouter(connect(mapStateToProps, null)(DbPaper));
THIS DOES NOT WORK
export default withRouter(withSnackbar(DbPaper), connect(mapStateToProps, null)(DbPaper));
any help is appreciated. Thanks
compose from redux allowed me to add multiple items.
import { compose } from "redux";
export default compose(
withRouter,
withSnackbar,
connect(mapStateToProps, null)
)(DbPaper);

TypeError: state.getIn is not a function

I'm implementing immutable on my react project, using it with redux, making state an immutable object using fromJS() function (from immutable library). In my reducer file, everything works, I receive an action, I can set the new value using setIn() function, and I can use getIn() function to access state.
But when I get state from connect() function, using mapStateToProps, even if console.log shows an apparently immutable object, I can't use immutable functions, like toJs() or getIn() here. I receive always this error: TypeError: state.getIn is not a function.
My index.js file
import React from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
import { template as templateAction } from './actions';
import withReducer from '../../../reducer/withReducer';
import templateReducer from './reducer';
export const Template = ({ name, template }) => (
<Button onClick={template}>
{name}
</Button>
);
Template.propTypes = {
name: PropTypes.string.isRequired,
template: PropTypes.func.isRequired,
};
export const mapStateToProps = (state) => {
console.log('state is equal to', state);
return (
{
name: state.getIn(['templateReducer', 'name']),
});
};
export const mapDispatchToProps = (dispatch) => ({
template: () => dispatch(templateAction()),
});
export default compose(
withReducer('templateReducer', templateReducer),
connect(mapStateToProps, mapDispatchToProps),
)(Template);
Result of console.log(state)
Result of console.log(state)
PS: When I don't use immutable state, everything works well.
Looks like state inside mapStateToProps function is an object with one property of 'templateReducer' that has a value of type Map.
I'm a bit rusty with my React knowledge, but maybe sharing the code for templateReducer would be helpful.
What does withReducer('templateReducer', templateReducer) do with the reducer function?
Seems like it is setting the state from the reducer to the key templateReducer before sending it to the mapStateToProps function. (maybe??)
Probably, changing this function to access the State before using immutable methods will remove the error.
export const mapStateToProps = (state) => {
console.log('state is equal to', state);
return (
{
name: state['templateReducer']?.getIn(['name']),
});
};
Use connect from griddle-react not react-redux.

mapDispatchToProps doesn't add actions to component

I'm tryng to use connect like in Redux docs (here) and I'm also using redux toolkit to work with redux (link)
import { connect } from 'react-redux'
import { actions } from '../../redux/actions';
import FormComponent from './Form';
const mapStateToProps = (state: any) => ({
item: 'state.item'
})
const mapDispatchToProps = { actions }
export default connect(
mapStateToProps,
mapDispatchToProps
)(FormComponent)
In my FormComponent I'm getting item props from mapStateToProps but actions are not passed down
My actions look like this
import { createAction } from '#reduxjs/toolkit';
const registerUser = createAction<Object, 'REGISTER_USER'>('REGISTER_USER');
export const actions = {
registerUser
}
And if I do console.log(actions) it is an object with function.
All the help will be appreciated.
This line:
mapDispatchToProps = { actions }
Is the equivalent to:
mapDispatchToProps = {
actions: {
registerUser
}
}
As per the docs bindActionCreators expects an object hash of action creators. In this example, you have nested yours under an actions property which (based on the source) will result in them being excluded from the returned props.
If you want the actions to be accessible directly from props then you can set mapDispatchToProps = actions, or just pass actions directly into connect e.g.
connect(mapStateToProps, actions)(FormComponent)

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

Redux store does not have a valid reducer

Haven't been able to find anything around here regarding this error:
"Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers."
My reducer
export default function FriendListReducer(state = {friends : []}, action) {
switch (action.type) {
case 'ADD_FRIEND':
return [
{ friends : action.payload.friend }, ...state.friends
]
default:
return state;
}
return state;
}
Combiner
import { combineReducers } from 'redux';
import { FriendListReducer } from './FriendListReducer';
const rootReducer = combineReducers({
friends: FriendListReducer
});
export default rootReducer;
My store config
import { applyMiddleware, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from '../reducers/reducers';
export default function configureStore(initialState = { friends: [] }) {
const logger = createLogger({
collapsed: true,
predicate: () =>
process.env.NODE_ENV === `development`, // eslint-disable-line no-unused-vars
});
const middleware = applyMiddleware(thunkMiddleware, logger);
const store = middleware(createStore)(rootReducer, initialState);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers/reducers', () => {
const nextRootReducer = require('../reducers/reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
Your import statement is incorrect. Either you use import { Foo } from 'bar' together with export Foo, or use import Foo from 'bar' if you export with export default Foo.
In other words, change either export default function FriendListReducer to export function FriendListReducer, or change the import statement from import { FriendListReducer } to import FriendListReducer.
If the object is empty.
export default combineReducers({
})
This error will show.
../reducers/reducers ? it's a strange naming
Anyway, it seems ../reducers/reducers doesn't return a reducer, if reducers is a directory, put a index.js inside, import each reducer and create a root reducer
import FriendListReducer from "./FriendListReducer"
const rootReducer = combineReducers({
friendList : FriendListReducer
})
export default rootReducer
Important!! you will have state.friendList in your state.
I hope it will help
store.js
FALSE
import { charactersSlice } from "./charactersSlice.js";
TRUE NOT USING {}
import charactersSlice from "./charactersSlice.js";
It looks like your top-level reducer function is using an array as its default value. Redux expects that the very top of your state will be an object, not an array. Try putting the array at a particular key in that object, like { friendList : [] }.
on above your codes
import { FriendListReducer } from './FriendListReducer';
const rootReducer = combineReducers({
friends: FriendListReducer
});
export default rootReducer;
instead of import { FriendListReducer } from './FriendListReducer';
just say import FriendListReducer from './FriendListReducer';
since FriendListReducer was exported with export default FriendListReducer and not export const FriendListReducer
Please check your combine reducer file It's empty......
you have forgot bind reducer here
import {combineReducers, createStore} from 'redux'
import listDataReducer from '../reducers/ListDataReducer'
const rootReducer = combineReducers({
listDataReducer, // Please add your reducer here
});
export default rootReducer;
I also faced the problem.
What I did was instead of:
combineReducers(productReducer, cartReducer)
I did:
combineReducers({ productReducer, cartReducer })
and it worked.
It expects a valid object for the store.

Resources