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
Related
I am trying to create an external functional component, which doesnt need to return any JSX, but just needs to send some internal React state to the Redux store. To keep my App.js clean i was trying to do this in an external file. For some reason the code below, where ChromeStoreToReduxStore is rendered as a child component, does not raise an error.
ChromeStoreToReduxStore.js
import { connect } from 'react-redux';
import * as actions from '../popupRedux/actions'
function ChromeStoreToReduxStore (props) {
console.log(props)
return null
}
export default connect(null, actions)(ChromeStoreToReduxStore);
App.js
function App (props) {
//state to keep track of local chrome store
const [chromeStore, updateChromeStore] = useState({})
...
console.log(ChromeStoreToReduxStore)
...
return (
<div className="App">
<ChromeStoreToReduxStore chromeStore = {chromeStore} />
</div>
);
}
export default App
However, calling the component in App.js like below gives the following error: TypeError: ChromeStoreToReduxStore is not a function
//whenever local store state changes send to redux store
useEffect(() => {
ChromeStoreToReduxStore(chromeStore);
}, [chromeStore])
The console.log below shows it appears to be a react memo. Can anyone explain why i cant call this function wrapped in connect like a normal function, and why it seems to have to be rendered as a React child?
I know i could just connect to the store inside App.js but would like to know why the above does not work.
I'm writing a unit test for a React component that is connected to Redux. One of the functions is the component is that it displays data if questionReducer.showquestions == true. I have attempted to re-create this functionality in the component by setting props with wrapper.setProps({ questionReducer: { showquestions: true } }). However, when I attempt this approach, I get the error:
ReactWrapper::setProps() expects a function as its second argument
How can I properly set the props for the connected Reducer in the component I am testing?
You should test the component alone, without being connected to Redux. That allows you to give props directly to component.
Example:
export class Component_ extends React.Component {
// your component logic here
}
const mapStateToProps = {
showQuestions: questionReducer.showquestions
}
const Component = connect(mapStateToProps)(Component_)
export default Component
And then in test you can just do this
const wrapper = shallow(<Component_ showQuestions={true} />
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 have a react component that wraps a class that renders WebGL using three.js with the DOM and connects mobx store value and it changes with the class lifecycle methods.
The passed in mobx store is only used outside of the components render function in lifecycle functions (componentDidMount, componentDidUpdate, ..). Noticed that when the store changes, the component doesn't trigger a rerender. But I make a useless read within the render functions, such as in the example below passing a triggerRerenderListenerProp={this.props.store.debugSettings.showStats} prop to the div, the component becomes active only to store.debugSettings.showStats changes.
Is there a way of making the component listen to store changes wihtout using the store itself in the render function?
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {observer} from 'mobx-react';
import MapPreview from 'classes/MapPreview';
import style from './Preview.css';
class Preview extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
imageUrl: PropTypes.string.isRequired
};
constructor (props) {
super(props);
this.containerEl = null;
}
componentDidMount () {
const options = {
debugSettings: this.props.store.debugSettings,
previewSettings: this.props.store.previewSettings
};
this.preview = new MapPreview(this.containerEl, options);
this.preview.setImage(imageUrl);
}
componentDidUpdate () {
this.preview.updateOptions({
debugSettings: this.props.store.debugSettings,
previewSettings: this.props.store.previewSettings
});
}
render () {
return (
<div
className={style.normal}
ref={(el) => { this.containerEl = el; }}
triggerRerenderListenerProp={this.props.store.debugSettings.showStats}
/>
);
}
}
export default observer(Preview);
The problem ultimately has two issues:
One, React is designed to only re-render when state or prop data changes.
Two, with mobx-react, I'm pretty sure the whole point is that the component won't re-render unless you dereference an observable value.
So while your props are technically changing, React doesn't do a deep object comparison of the props.
What you might try is setting options as internal component state -- that might force a re-render even though nothing in the render method would have changed.
The caveat here is that the updated props (from your store) might be too deeply nested as to force React to re-render even while updating internal state. You might also need to piggy-back on shouldComponentUpdate();
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