I am working on a first React-Redux app. I have the following container:
import { connect } from 'react-redux'
import Visualization from '../components/visualization'
// http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components
const mapStateToProps = (state) => state; // identity transform, for now...
function mapDispatchToProps(dispatch) {
return {
stepForward: () => dispatch('STEP_FORWARD')
};
}
const VisualizationContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Visualization);
export default VisualizationContainer;
And the following subsequent component:
import React from 'react'
export default class Visualization extends React.Component {
render() {
console.log(this.props.stepForward());
return <div>"HELLO WORLD"</div>;
}
}
However, when I run this application I error out with:
Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
The problem, marked with XXX, is that stepForward exists, but explodes when executed. What is the error here?
It is just as the error says. An action should be an object and no other type. You're trying to dispatch a string as an action, which is invalid. Instead, you should use an object with a type property set to the action's type string.
Change your mapDispatchToProps function to:
function mapDispatchToProps(dispatch) {
return {
stepForward: () => dispatch({
type: 'STEP_FORWARD'
})
};
}
and it should work.
You should actually go a bit further than that and make all your actions follow a standardized template. A nice one a lot of React devs follow is flux-standard-action.
Related
How to use redux in class component using react with typescript.
1.) useDispatch,useSelector how to use it in class base components react typescript
And here I get the data from store.tsx using useSelector but this is a class component(App.tsx)
then, I dispatched here(App.jsx)
You have to use higher order component connect which is provided by react-redux library.
first import it
import { connect } from "react-redux";
then to access state use the function
const mapStateToProps = (props) => {
return {
api: props.api,
};
};
and for action use
const mapDispatchToProps = (dispatch) => {
return {
action: () => dispatch(action),
};
};
and at the end export it like
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
to use it simply access through props, for example
const data = props.api
and same for dispatch
function addData () {
props.action()
}
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.
I am creating a page in React. Lets say for eg. "Conatct us" page. This whole component must be reusable. So that other teams can use it as it is. This component will have its own redux store and api calls using axios.
What I want to confirm that if I export this "Contact Us" module as npm package, will it work fine for other teams? Why I am asking this is because other teams project will have their own redux store and axios instance. And I think we can have only one redux store in an app and maybe one axios interceptors (I may be wrong about axios though)
Could anyone help me out, what can be done in this case? One thing is sure that I will have to export this whole component as npm package.
I'm going to answer here to give you more details:
Let's say your component looks like this:
AboutUs:
import React, { Component } from "react";
import PropTypes from "prop-types";
export class AboutUs extends Component {
componentDidMount() {
const { fetchData } = this.props;
fetchData();
}
render() {
const { data, loading, error } = this.props;
if (loading) return <p>Loading</p>;
if (error) return <p>{error}</p>;
return (
// whatever you want to do with the data prop that comes from the fetch.
)
}
}
AboutUs.defaultProps = {
error: null,
};
// Here you declare what is needed for your component to work.
AboutUs.propTypes = {
error: PropTypes.string,
data: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
}),
fetchData: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
};
This component just takes a few props in order to work and the fetchData function will be a dispatch of any redux action.
So in one of the apps that are going to use the component library, assuming that they have their own store, you could do something like this.
In the component where you're planning to use the AboutUs component.
import React from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
// this is the action that performs the data fetching flow.
import { fetchAboutUs } from "redux-modules/aboutUs/actions";
// The component that is above
import { AboutUs } from "your-component-library";
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
fetchData: fetchDashboard,
},
dispatch
);
};
const mapStateToProps = state => ({
loading: state.aboutUsReducer.loading,
error: state.aboutUsReducer.error,
data: state.aboutUsReducer.data,
});
const ReduxAboutUs = connect(
mapStateToProps,
mapDispatchToProps
)(AboutUs);
// Use your connected redux component in the app.
const SampleComponent = () => {
return <ReduxAboutUs />
}
This ensures that your component can work out of the box without redux, because you can explicitly use it without the redux dependency and just pass regular props and it will continue working. Also if you have different applications where you are going to use it you will have the control of which part of the store you want to use to inject the props for this component. Proptypes are quite useful here, because we're enforcing a few props in order let the devs what do we need to pass in order for the component to work properly.
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 trying to use react-redux-loading-bar to show a loading bar during fetching data from API servers, I don't use promise middleware so I decided to use it without, the example says do this
import { showLoading, hideLoading } from 'react-redux-loading-bar'
dispatch(showLoading())
// do long running stuff
dispatch(hideLoading())
And it gives me this.
Uncaught ReferenceError: dispatch is not defined
I had similar issues with other libraries and gave up that time, this time I want to actually understand how this works, so any info is greatly appreciated. Heres the code that causing the error, speicifc function and class names stripped.
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { showLoading, hideLoading } from 'react-redux-loading-bar'
import * as xxxxxActions from '../../actions/xxxxx'
class xxxxxx extends React.Component {
constructor(props) {
super(props)
this.handleclick = this.handleclick.bind(this)
}
handleclick(){
dispatch(showLoading())
asynchronousGetFunction( target_url, function (data) {
dispatch(hideLoading())
})
}
render() {
return <li onClick={this.handleclick}>yyyyyyy</li>
}
}
function mapStateToProps( state ){
return {
}
}
function mapDispatchToProps(dispatch, state) {
return {
xxxxxActions: bindActionCreators( xxxxxActions, dispatch )
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(xxxxxx)
Once you connect your component, dispatch becomes a prop. The same applies for xxxxxActions...
In that case, the handle would be:
handleclick(){
this.props.dispatch(...)
}
You need to pass dispatch function to your props:
function mapDispatchToProps(dispatch, state) {
return {
xxxxxActions: ....,
showLoading: function () {
dispatch(showLoading());
},
hideLoading: function () {
dispatch(hideLoading());
},
};
}
Then, use it in your component:
this.props.showLoading();
...
this.props.hideLoading();
You don't need use "dispatch" in components. Bind your functions with dispatch in mapDispatchToProps.
Read more about mapDispatchToProps.