How to use multiple multiActions in react/redux? - reactjs

I am importing multiple multiactions into my component and using connect. I have import one multaction and it works just fine, but I can't figure out how to do multiple. Here is my code.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import * as actions from '../../actions/posts_actions';
import * as actionsIndex from '../../actions/index';
import * as actionsComments from '../../comments_actions';
function mapStateToProps({ posts }, ownProps) {
return { post: posts[ownProps.match.params.id] };
}
export default connect(mapStateToProps, actions)(ShowPosts);

You can pass create a mapDispatchToProps that uses bindActionCreators and pass that as a second argument to connect instead of actions.
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators(Object.assign({}, actions, actionsIndex,
actionsComments), dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(ShowPosts);

Related

React/Redux export default connect() doesn't seem to be connected to Provider

[Solved] Check my answers
I'm learning MERN Stack via a youtube playlist https://www.youtube.com/watch?v=TO6akRGXhx8. I'm stuck when i reached the 28:04 where he forgot to connect his component with 'react-redux'. I followed how he resolve it but well, for some reason mine doesn't seem to be connected. No props was pass by to my ItemModal component. So i spent 3hrs to debug and lastly conclude that i found it weird that only when the js is named ShippingList, will connect() works... When I renamed ShippingList to another name and update the references, it doesn't work anymore... Please refer to below for some of the snippet
I dont think i need to identify a component to the store when creating it.. so im stupefied now..
Was wondering if u guys can replicate it, please find my repo
https://github.com/AmeDin/mern
ShoppingList.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
export class ShoppingList extends Component {
render() {
console.log(this.props)
console.log(this.state)
//const { items } = this.props.item;
return (
<div>
</div>
)
}
}
const mapStateToProps = (state) => ({
item: state.item
})
export default connect()(ShoppingList);
ShoppingListOne.js
import React, { Component } from 'react'
import { connect } from 'react-redux';
export class ShoppingListOne extends Component {
render() {
console.log(this.props)
console.log(this.state)
//const { items } = this.props.item;
return (
<div>
</div>
)
}
}
const mapStateToProps = (state) => ({
item: state.item
})
export default connect()(ShoppingListOne);
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore, applyMiddleware, compose } from 'redux'
import rootReducer from './reducers/index'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import * as serviceWorker from './serviceWorker';
const middleware = [thunk];
const store = createStore(rootReducer,
compose(
applyMiddleware(thunk)
)
);
ReactDOM.render(<Provider store={store}><App /></Provider>,
document.getElementById('root'));
serviceWorker.unregister();
Screenshot of console.log: https://i.stack.imgur.com/FPBBs.png
Further testing
ShoppingListOne
const mapStateToProps = (state) => ({
item: state.item
})
const mapDispatchToProps = (dispatch) => {
console.log(dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(ShoppingListOne);
ShoppingList
const mapStateToProps = (state) => ({
item: state.item
})
const mapDispatchToProps = (dispatch) => {
console.log(dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(ShoppingList);
No functions seems to be called for ShoppingListOne.
ShoppingList has a function called line26, 3rd row of console.
https://i.stack.imgur.com/WxwRm.png
You need to pass mapStateToProps function as first argument to connect in order to make these value available to the component connected to redux store
. Connect without any arguments don't do anything except make dispatch available as a prop to the connected component
const mapStateToProps = (state) => ({
item: state.item
})
export default connect(mapStateToProps)(ShoppingListOne);
and
const mapStateToProps = (state) => ({
item: state.item
})
export default connect(mapStateToProps)(ShoppingList);
Also you need to make sure that you are imported the connected component which is ShoppingListOne exported as a default export rather than a named export
You import would look like
import ShoppingListOne from './path/to/ShoppingListOne';
You must pass mapStateToProps and mapDispatchToProps to connect, so that it can create a wrapper which has access to redux store.
export default connect(mapStateToProps, mapDispatchToProps)(ShoppingList);
export default connect(mapStateToProps, mapDispatchToProps)(ShoppingListOne);
Found out the issue...
The import statement seems to play its role to the connection..
Error
import { ShoppingList } from './components/ShoppingList';
import { ItemModal } from './components/ItemModal';
Correct
import ShoppingList from './components/ShoppingList';
import ItemModal from './components/ItemModal';
Anyone know the differences? Is there a post out there answering it i wonder?

react-redux shopping-cart example

I am new to react and react-redux. I am learning the redux example about the Shopping-cart, here is the link enter link description here
And I have two questions:
In containers/ProductsContainer.js, the connect passed the { addToCart } like this:
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { addToCart } from '../actions'
import { getVisibleProducts } from '../reducers/products'
import ProductItem from '../components/ProductItem'
import ProductsList from '../components/ProductsList'
......
export default connect(
mapStateToProps,
{ addToCart }
)(ProductsContainer)
I do not understand how the { addToCart } can be passed into connect
2.The addToCard is like this:
export const addToCart = productId => (dispatch, getState) => {
if (getState().products.byId[productId].inventory > 0) {
dispatch(addToCartUnsafe(productId))
}
}
where does getState parameter come from?
Definition of connect function is
export default connect(
mapStateToProps,
mapDispatchToProps
)(SomeComponent)
Here { addToCart } passed directly in place of mapDispatchToProps. It is like {addToCart : addToCart } which is called shorthand syntax.

How to wrap multi actionCreators into one props?

I'm getting the following error:
Uncaught TypeError: this.props.dispatch is not a function
Here's my component:
import React from 'react';
import PropTypes from 'prop-types';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as jobTitleSkillsActions from '../../actions/jobTitleSkillsActions';
import SkillList from './SkillList';
import * as userPositionActions from '../../actions/userPositionActions';
class SkillPage extends React.Component {
componentDidMount() {
this.props.dispatch(userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id; this.props.dispatch(jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
.....
const mapStateToProps = state => {
return {
job_title_skills: state.job_title_skills,
user_positions: state.user_positions
};
};
function mapDispatchToProps(dispatch) {
return {
actions: {
userPositionActions: bindActionCreators(userPositionActions, dispatch),
jobTitleSkillsActions: bindActionCreators(jobTitleSkillsActions, dispatch),
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SkillPage);
What am I doing wrong here?
Per my comment, this would be the correct syntax to bind multiple sub-objects worth of action creators:
function mapDispatchToProps(dispatch) {
return {
userPositionActions : bindActionCreators(userPositionActions, dispatch),
jobTitleSkillsActions: bindActionCreators(jobTitleSkillsActions, dispatch),
}
}
Since you have already used mapDispatchToProps, dispatch wont be available to the component as a prop. Since you have used mapDisptachToProps, the jobs actions will be available as props and you can use them like
componentDidMount() {
this.props.actions.userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id;
this.props.actions.jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
However you can simplify it further like
function mapDispatchToProps(dispatch) {
return bindActionCreators({userPositionActions, jobTitleSkillsActions}, dispatch)
}
...
componentDidMount() {
this.props.userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id;
this.props.jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
As you have provided mapDispatchToProps to connect function, dispatch is not passed as prop to your component.
Your componentDidMount code should be like this:
componentDidMount() {
const actions = this.props.actions
actions.userPositionActions.loadUserPositions()
var job_title_id = this.props.user_positions[0].job_title_id;
actions.jobTitleSkillsActions.loadJobTitleSkills(job_title_id)
}

Reusable components Redux

I have a reusable component having its own action and reducer that i then use in another component.
Component AddToCart
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import Button from 'environment/atoms/button'
import * as AppIndexActionsCreators from 'environment/AppIndexActionsCreators'
const AddToCart = (props)=>{
let boundActionCreators = bindActionCreators(AppIndexActionsCreators)
return(
<Button
txt="Add To Cart"
{...boundActionCreators}
/>
)
}
export default AddToCart;
I pass it in
import React from 'react'
import { Link } from 'react-router'
import ProductDesc from '../Molecules/ProductDesc'
import ProductImg from 'environment/sharedMolecules/ProductImg'
import AddToCart from 'environment/sharedMolecules/AddToCart'
const Product = (props) => {
const product = props.product;
return (
<div>
<Link to={`/productDesc/${product.id}`}>
<ProductDesc {...props} />
<ProductImg {...props}
size="small"
/>
</Link>
<AddToCart/>
</div>
)
}
Product.propTypes = {
displayProduct: React.PropTypes.func,
product: React.PropTypes.object
};
On Click on AddToCart nothing happens where it should print a console.log as defined in my Reducer...when inspecting the AddToStore component in the browser i can see in the props that the component sees the AddToCart fn defined in the Action file......
looks like Action is not dispatched to the reducer...how to fix this ?
Use redux connect decorator.
Firstly, import it from react-redux bindings:
import { connect } from 'react-redux';
Then decorate your component with it!:
connect(mapStateToProps, mapDispatchToProps)(AddToCart)
Where functions in arguments should be defined somewhere like:
function mapStateToProps(state) {
return {
// someStoreVar: state.someStore.someStoreVar
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ ...AppIndexActionsCreators }, dispatch);
}
First one is to pass store state to props, second one is to pass
actions to props. They are fully optional and if you don't need the
store nor the actions bound to props you can omit them with null
like:
connect(null, mapDispatchToProps)(AddToCart)
Finally you will wan't this decorated component to be exported by
default instead of not-decorated one:
export default connect(mapStateToProps, null)(AddToCart)
At this moment you will be able to dispatch any actions or to access any store vars from props inside the component. This is a default technique to do this in react and you will use this quite alot. If this still sounds confusing feel free to ask for explanations.
I ended up rewriting my component as suggested by Syberic
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import Button from 'environment/atoms/button'
import * as AppIndexActionsCreators from 'environment/AppIndexActionsCreators'
const AddToCart = (props) => {
// let boundActionCreators = bindActionCreators(AppIndexActionsCreators)
return(
<Button
txt="Ajouter au panier"
clickAddToCart = { props.addToCart }
/>
)
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(AppIndexActionsCreators, dispatch);
}
export default connect(null,mapDispatchToProps)(AddToCart)
Additionally i corrected my Reducer where there was a syntax error. I did not put 'export default' but only 'export'
import { ADD_TO_CART } from './ActionTypes'
export default function cart(state=[],action){
console.log();
switch (action.type){
case ADD_TO_CART:
console.log('yeah');
return state;
default:
return state;
}
}

How to use react-router-redux routeActions?

I'm trying to modify the example code of react-router-redux.
https://github.com/rackt/react-router-redux/blob/master/examples/basic/components/Home.js
this is my Home.js
class Home extends Component {
onSubmit(props) {
this.props.routeActions.push('/foo');
}
}
I also have a mapDispatchToProps for it.
function mapDispatchToProps(dispatch){
return bindActionCreators({ routeActions },dispatch);
}
When i called onSubmit function, I got an error
Uncaught TypeError: this.props.routeActions.push is not a function
If i remove this.props in onSubmit, the key in the URL changed but its still on the same page.
From localhost:8080/#/?_k=iwio19 to localhost:8080/#/?_k=ldn1ew
Anyone know how to fix it?
Appreciate it.
I don't think routeActions are passed as props. What you want to do is this:
import { routeActions } from 'react-router-redux'
this.props.dispatch(routeActions.push('/foo'));
To provide a little more explicit answer and the answer to my own question in the comment.
Yes you can do
import { routeActions } from 'react-router-redux'
this.props.dispatch(routeActions.push('/foo));
However as I mentioned mapDispatchToProps will override this.
To fix this you can bind the routeActions like so:
import { bindActionCreators } from 'redux'
import { routeActions } from 'react-router-redux'
function mapDispatchToProps(dispatch) {
return {
routeActions: bindActionCreators(routeActions, dispatch),
}
}
export default connect(null, mapDispatchToProps)(YourComponent)
Now you can do: this.props.routeActions.push('/foo')
Just FYI this can be done even neater
function mapDispatchToProps(dispatch) {
return {
...bindActions({routeActions, anotherAction}, dispatch)
}
}
As for react-router-redux v4:
import { push } from 'react-router-redux';
export class ExampleContainer extends React.Component {
static propTypes = {
changeRoute: React.PropTypes.func,
};
function mapDispatchToProps(dispatch) {
return {
changeRoute: (url) => dispatch(push(url)),
dispatch,
};
}
}
export default connect(null, mapDispatchToProps)(ExampleContainer);
then:
this.props.changeRoute('/foo');
I was able to get it working by doing this
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { routeActions } from 'react-router-redux'
export const Cmpt = React.createClass({ //code })
function mapDispatchToProps(dispatch) {
return bindActionCreators({
push: routeActions.push,
//all your other action creators here
}, dispatch)
}
export const CmptContainer = connect({}, mapDispatchToProps)(Cmpt)
Then you can use push via props
this.props.push('some/cool/route')
More Concisely
For these examples, I'd curry the mapDispatchToProps function like so:
bindActionDispatchers.js
import { bindActionCreators } from 'redux'
/** curries mapDispatchToProps with bindActionCreators to simplify React action dispatchers. */
export default function bindActionDispatchers(actionCreators) {
if(typeof actionCreators === 'function')
return (dispatch, ownProps) => bindActionCreators(actionCreators(ownProps), dispatch)
return dispatch => bindActionCreators(actionCreators, dispatch)
}
Foo.js
import React from 'react'
import bindActionDispatchers from './bindActionDispatchers'
import { routeActions } from 'react-router-redux'
import * as appActions from './actions'
const Foo = ({ routeActions ...appActions }) => (
<div>
<button onClick={routeActions.push('/route')}>Route</button>
<button onClick={appActions.hello('world')}>Hello</button>
</div>
)
export default connect(null, bindActionDispatchers({ routeActions, ...appActions }))(Foo)
I packaged this into a lightweight npm package bind-action-dispatchers complete with unit tests and sanity checks. To use this version install with:
npm i -S bind-action-dispatchers
and import the function with:
import bindActionDispatchers from 'bind-action-dispatchers'

Resources