I have checked sample code of reduxForm with initialized value, the only difference between their code and my code is the following chunk of code..
My Code (Doesn't work with initialValues)
function mapStateToProps(state) {
return{
initialValues: state.account.data
};
}
export default reduxForm({
form:'initializeFromState'
})(connect(mapStateToProps,{load: loadAccount})(InitializeFromStateForm));
Their code (Works with InitialValues) Taken from here
InitializeFromStateForm = reduxForm({
form: 'initializeFromState', // a unique identifier for this form
})(InitializeFromStateForm);
// You have to connect() to any reducers that you wish to connect to yourself
InitializeFromStateForm = connect(
state => ({
initialValues: state.account.data, // pull initial values from account reducer
}),
{ load: loadAccount }, // bind account loading action creator
)(InitializeFromStateForm);
export default InitializeFromStateForm;
I changed their code for connect() and reduxForm with mine, interestingly the initialValues stopped working, now my question is are both the code different? if different what is wrong in my code?
Thanks.
Yeah there is a slight difference, you are wrapping the component with connect and then with ReduxForm, However it should be the other way round
Change your code to
export default connect(mapStateToProps,{load: loadAccount})(reduxForm({
form:'initializeFromState'
})(InitializeFromStateForm));
and it should work
The difference is in the order in which the react-redux connect HoC, and the redux-form HoC wrap each other.
In your code redux-form wraps the connect HoC, and the initialValues are not passed to the form, but to the internal component. The form is initialized with the values, and the internal component (yours) ignores them.
Props flow: redux-form -> connect - initialValues -> component
In their code connect wraps redux-form, and the initialValues are passed as to the redux-form HoC (the form). The form is initialized with the values.
Props flow: connect - initialValues -> redux-form -> component
Related
I'm connecting store to the Image component using redux connect. I only expect to retrieve there (in the Image component) one prop from Redux: "images". But I also get an additional prop: "dispatch".
import { connect } from "react-redux";
import Image from "./Image";
import { imagesSelector } from "units/images/selectors";
const mapStateToProps: MapStateToPropsParam = (state) => ({
images: imagesSelector(state),
});
export default connect(mapStateToProps)(Image);
In the Image component I get:
{ images: [...], dispatch: function(){...}}
The question:
How to exclude dispatch from being passed to my component?
From the react-redux docs:
If you don't specify a second argument to connect(), your component
will receive dispatch as a prop by default.
If you don't want this behavior, you can instead pass a second argument to the connect function, which would be the mapDispatchToProps argument.
It can be either a function or an object.
We recommend always using the “object shorthand” form of
mapDispatchToProps, unless you have a specific reason to customize the
dispatching behavior.
Note that:
Each field of the mapDispatchToProps object is assumed to be an action creator
Your component will no longer receive dispatch as a prop
Docs: https://react-redux.js.org/using-react-redux/connect-mapdispatch
export default connect(mapStateToProps, null)(Image);
add null as a second parameter and you're good to go
Does anyone know how to wrap a React component with React.memo when one is using the connect function from react-redux?
For example, how would you modify the following?
let Button = (props: Props) => (
<button onClick={props.click}>{props.value}</button>
);
Button = connect(
mapStateToProps,
mapDispatchToProps
)(Button);
I've tried:
let Button = React.memo((props: Props) => (
<button onClick={props.click}>{props.value}</button>
));
Button = connect(
mapStateToProps,
mapDispatchToProps
)(Button);
However, the function returned by connect expects a component to be passed so it errors with:
Uncaught Error: You must pass a component to the function returned by
connect. Instead received {"compare":null}
React.memo is nothing but a HOC, so you can just use:
Without memo:
connect(
mapStateToProps,
mapDispatchToProps
)(Button);
With memo:
connect(
mapStateToProps,
mapDispatchToProps
)(React.memo(Button));
And even wrap to connect: (This should be the solution with connect)
React.memo(
connect(
mapStateToProps,
mapDispatchToProps
)(Button)
);
Like we do with withRouter: withRouter(connect(...)())
Same issue here. Fixed by upgrading react-redux to version 5.1.0.
Your solution should work (I didn't try copy-pasted like that), but you also have to update react-redux to the latest version.
By the way, I think the proper implementation of React.memo within many HOC would be to be the closest to the component itself : the goal of React.memo is to check if all the new props received by the component are the same as the last props. If any HOC transforms or adds any props to the component - which connect does by mapping the Redux store to the props, React.memo should be aware of it in order to decide wether or not to update the component.
So I would go for something like that :
//import what you need to import
const Component = props => <div>{/* do what you need to do here */}</div>
export default compose(
connect(mapStateToProps, dispatchToProps),
/* any other HOC*/
React.memo
)(Component);
Codesandbox demo
As the error message says, you need to pass a component to the returned function from connect.( which means the second pair of () in connect()() )
As React.Memo returns a component, pass it into the second function of connect.Here's how you can do this.
export const MemoizedDemoComponent = connect(mapStateToProps)(React.memo(DemoComponent);
Demo component:
import React from "react";
import { connect } from "react-redux";
const DemoComponent = props => (
<div>
<p>My demo component fueled by: {props.fuel}!</p>
<p>Redux: {props.state}</p>
</div>
);
const mapStateToProps = state => ({
state: "your redux state..."
});
// create a version that only renders on prop changes
export const MemoizedDemoComponent = connect(mapStateToProps)(
React.memo(DemoComponent)
);
For a working example check also codesandbox.
For someone who want to know why react-redux throw this error.
For me, I used version 5.0.7, react-redux/src/components/connectAdvanced.js line: 92
invariant(
typeof WrappedComponent == 'function',
`You must pass a component to the function returned by ` +
`${methodName}. Instead received ${JSON.stringify(WrappedComponent)}`
);
After upgrading this code is changed to :
invariant(
isValidElementType(WrappedComponent),
`You must pass a component to the function returned by ` +
`${methodName}. Instead received ${JSON.stringify(WrappedComponent)}`
);
How to check the WrappedComponent is changed to isValidElementType(WrappedComponent) which is exposed by react-is
So, yarn update react-redux to the version that mentioned by #Maxime Chéramy at least
I have some experience with ReactJS but now I am trying to start using Redux and I have encoutered several problems. I already know how to create actions, consts, reducers, how to connect them to one single store, but I don't actually now how to use it with React. For example I have a form to gather user's data and I want it all passed to Redux store. So I guess the main question would be how do I trigger the action in ReactJS?
when using react-redux, you'll get a component enhancer called connect.
class Component extends React.Component {
render() {
return (
<button onClick={this.props.onClickButton}>
{this.props.a}
</button>
)
}
}
export default connect(function mapStateToProps(state) {
return { a: state.store.a }
}, { onClickButton: incrementAction })(Component)
What I'm doing here is taking a global store value (state.store.a - state is the global store, .store is the store from a combined store, and a is the value), and telling the React component to listen for changes on this variable (transparently through connect).
Additionally, I'm wrapping an action creator incrementAction (and renaming it to onClickButton). If you're using a middleware like redux-thunk, this will automatically pass in store.dispatch as an arg. Otherwise, this is a standard action creator.
both of these will be available inside the component as props (the args are descriptively named mapStateToProps and mapDispatchToProps)
You'll want to use react-redux. For example, here's a small counter:
import { connect } from "react-redux";
import { increment } from "actions";
import PropTypes from "prop-types";
import React from "react";
function counter ({ count, increment }) {
return <button onClick={increment}>
{count}
</button>;
}
counter.propTypes = {
count: PropTypes.number.isRequired,
increment: PropTypes.func.isRequired
};
export default connect(
(state) => ({
count: state.data.count
}),
{ increment }
)(counter);
The (state) => ({ }) bit passes a property called count to the component's props. The { increment } passes your increment function in the props.
Be sure to include the { increment } part in the connect; if you don't, your redux action won't be dispatched.
To bind redux to react there is a package called react-redux. The description of which is official react bindings for redux.
You can connect the actions to react by using mapDispatchToProps, which will map your actions as props. Then you can call those actions as props. When you call those actions as props, the actions will be triggered and redux state will change.
To access the state you have to use mapStateToProps, which will give you the state as props.
You can use connect method to connect mapStateToProps and mapDispatchToProps to react.
I think it would be easier if you do a tutorial. This is a tutorial by Dan Abramov, creator of Redux.
I am currently building an app with React, React Router and React Redux
Versions:
React - v15.5.4
React Router - v4.0
React Redux - v.5.0.6
I am new to React and even newer to Redux and right when I got my head around the connect HOC I started to have this error that I cant seem to figure out.
When I connect a component to my redux store after a <switch> element and some <Route> elements. My connect within that returns my props as false boolean values where as the component within the connect has the correct props.
See code and error below for example.
Component
UserDashboardPage = connect(state => {
console.log("STATE", state);
return {
user: state.user.user,
userAuth: state.user.userAuth,
userFetched: state.user.fetched
};
})(UserDashboardPage);
UserDashboardPage.propTypes = {
user: PropTypes.shape(),
userAuth: PropTypes.shape(),
userFetched: PropTypes.boolean,
dispatch: PropTypes.func
};
CONSOLE LOG STATE
Connect with boolean prop values
Component with correct props
ERROR:
You are overwriting the local UserDashboardPage variable with the result of calling connect(). You then set PropTypes on the component returned by connect().
While you can do that, what you want in this case is to set the PropTypes of the wrapped component, not the wrapper component. Just swapping the order of execution will do it:
UserDashboardPage.propTypes = {
};
UserDashboardPage = connect(state => {
...
})(UserDashboardPage);
But you may want to consider using a different variable name for one component or the other, e.g.
UserDashboardPage.propTypes = {
};
const ConnectedUserDashboardPage = connect(state => {
...
})(UserDashboardPage);
This is usually not a problem since most people just immediately export the connected component as the default export:
export default connect(...)
The false values you're seeing are from React assigning default values to those props that failed validation. And they will always fail validation since those props are pulled from context, not passed down as normal props.
why are you passing UserDashboardPage into connect? This should be your non connected component
How do I tell react to take constraints from a prop passed toLoginForm from its parent?
export default reduxForm({
form: 'LoginForm',
validate: formValidator(constraints)
})(LoginForm)
In the above working code, constraints is not declared outside of the React components.
validate can be passed to the form as a prop instead of the reduxForm config. So you'd pass it when you instantiate:
<LoginForm validate={formValidator(constraints)} />
http://redux-form.com/7.0.3/docs/api/ReduxForm.md/