What does this syntax mean export default connect(mapStatetoProps, mapDispatchToProps)(LandingComponent) - reactjs

A module I have contains the following line. connect seems to have two pairs of parentheses. What does this mean?
export default connect(mapStatetoProps, mapDispatchToProps).
(LandingComponent)
Tried to lookup various documentation
import { connect } from 'react-redux'
import { LandingComponent } from './Landing'
const mapStatetoProps = state => {
return {}
}
const mapDispatchToProps = dispatch => {
return {}
}
export default connect(mapStatetoProps, mapDispatchToProps)
(LandingComponent)
Expect to understand what the syntax means.

The second set of parentheses is because connect(..., ...) returns a function. This function is a component decorator which is why it is called with the landing component class.
If you split it up it might become clearer:
const decorate = connect(mapStatetoProps, mapDispatchToProps);
const ReduxConnectedLandingComponent = decorate(LandingComponent);
export default ReduxConnectedLandingComponent;
In this case decorate is a function that accepts a single component and returns a component. i.e. it takes the plain component and returns a smarter one which pulls props from the nearest provided store in the hierarchy.

Example:
const TodoItem = ({ todo, destroyTodo }) => {
return (
<div>
{todo.text}
<span onClick={destroyTodo}> x </span>
</div>
)
}
const mapStateToProps = state => {
return {
todo: state.todos[0]
}
}
const mapDispatchToProps = dispatch => {
return {
destroyTodo: () =>
dispatch({
type: 'DESTROY_TODO'
})
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoItem)
mapStateToProps and mapDispatchToProps are both pure functions that are provided the stores “state” and “dispatch” respectively. Furthermore, both functions have to return an object, whose keys will then be passed on as the props of the component they are connected to.
In this case, mapStateToProps returns an object with only one key : “todo”, and mapDispatchToProps returns an object with the destroyTodo key.
The connected component (which is exported) provides todo and destroyTodo as props to TodoItem.
Source: https://www.sohamkamani.com/blog/2017/03/31/react-redux-connect-explained/

Related

would like to know how to write the functional components to class components in react-redux

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getTodoList } from "./redux/action/todoList";
Using functional components I have written want the same how we can write in class components
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTodoList());
}, []);
const todoList = useSelector((state) => state.todoList);
console.log(todoList);
return (
<div className="App">
{todoList.data.length > 0
? todoList.data.map((obj) => {
return (
<>
<div className="card">
<div className="content">
<div className="header">{obj.title}</div>
</div>
</div>
</>
);
})
: ""}
</div>
);
}
export default App;
To connect the redux store to your component, you can use connect API. As per the documentation
The connect function takes two arguments, both optional:
mapStateToProps: called every time the store state changes. It receives the entire store state and should return an object of data this component needs.
mapDispatchToProps: this parameter can either be a function or an object.
If it’s a function, it will be called once on component creation. It will receive dispatch as an argument and should return an object full of tasks that use dispatch to dispatch actions.
If it’s an object full of action creators, each action creator will be turned into a prop function that automatically dispatches its action when called. Note: We recommend using this “object shorthand” form.
Your code will be changed like this.
const mapStateToProps = (state, ownProps) => ({
todoList: state.todoList
});
const mapDispatchToProps = (dispatch) => ({
todoList: dispatch(getTodoList())
});
// `connect` returns a new function that accepts the component to wrap:
const connectToStore = connect(mapStateToProps, mapDispatchToProps);
// and that function returns the connected, wrapper component:
const ConnectedApp = connectToStore(App);
// We normally do both in one step, like this:
connect(mapStateToProps, mapDispatchToProps)(App);

this.props... is not a function react react-redux

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.

Multiple actionCreators in single component

I want to have multiple actionCreators dispatched into one component. I know you can do this with state
export default connect(
(state: ApplicationState) => Object.assign({}, state.search, state.resources),
ResourcesState.actionCreators// i have another actionCreator I want to add
)(Home) as typeof Home;
But not sure the syntax to do this with actionCreators. I have read into
mapDispatchToProps
But not sure how to implement.
There are a few ways to set up dispatching of Redux actions in React components:
Use connect(mapState)(MyComponent). By default, your component will be given props.dispatch, and you can call props.dispatch({type : "SOME_ACTION"}).
Pass a mapDispatchToProps function as the second argument to connect. Inside, you can create new function references that call dispatch:
function mapDispatchToProps(dispatch) {
return {
addTodo : (text) => dispatch({type : "ADD_TODO", text})
}
}
You can also use the Redux bindActionCreators utility instead:
function mapDispatchToProps(dispatch) {
return bindActionCreators({addTodo, toggleTodo}, dispatch);
}
Finally, you can pass an object full of action creators directly to connect:
const actions = {addTodo, toggleTodo};
export default connect(mapState, actions)(MyComponent);
I highly recommend the fourth approach, which I also talk about in my blog post Idiomatic Redux: Why Use Action Creators?.
mapDispatchToProps is the second argument in connect. So for example:
import customAction from 'actions-directory-in-your-app'
const mapStateToProps = () => {} // no implementing anything for example purposes
const mapDispatchToProps = () => ({ customAction })
const ConnectedContainer = connect(
mapStateToProps,
mapDispatchToProps
)(YourContainer)
customAction becomes now a prop in YourContainer so you can use it the same way other props within your component.
the Second argument to connect takes an object or a function so you can add
export default connect(
(state: ApplicationState) => Object.assign({}, state.search, state.resources),
{
ResourcesState.actionCreators,
some_other_action_creators,
some_more
}
)(Home) as typeof Home;
Also read through this answer on Stackoverflow for more information on how to use action creators
Why is there no need for a mapDispatchToProps function here?
The second argument to connect takes an object, so you can use of ES6 syntax and avoid the use of mapDispatchToProps.
import { yourAction } from './your_actions_folder'
class Home extends Component{
....
//For dispatch a action you only call the action Creator
this.props.yourAction()
}
export default connect(mapStateToProps,{yourAction})(Home)

What to use for wrapping connect function only in react-redux apps?

Do I need to use HOC if I only need to change mapDispatchToProps and mapStateToProps but not change the presentational component?
For example:
export class SomeContainer extends Component {...}
const mapStateToProps = state => {
//IMPORT HERE STATE
};
const mapDispatchToProps = (dispatch, ownProps) => ({
...someFn,
//IMPORT HERE OTHER DISPATCH FN
});
And reuse it in other components by changing connectFn.
I just wrapped it up in different components
import React, {Component, PropTypes} from "react";
import SomeContainer from "./index";
export class ReuseSomeContainerA extends Component {
render() {
return (
<SomeContainer {...this.props}/>
);
}
}
const mapDispatchToProps = (dispatch, ownProps) => ({
//...customDispatchsA
});
export const enhance = connect(() => state => ({
//customPropsA
}), mapDispatchToProps);
export default enhance(ReuseSomeContainerA );
And likewise with another used SomeContainer
export class ReuseSomeContainerB extends Component {
render() {
return (
<SomeContainer {...this.props}/>
);
}
}
const mapDispatchToProps = (dispatch, ownProps) => ({
//...customDispatchsB
});
export const enhance = connect(() => state => ({
//customPropsB
}), mapDispatchToProps);
export default enhance(ReuseSomeContainerB);
In addition to these functions do not change anything.
Is there a way of more correct composition mapDispatchToProps and mapStateToProps only?
I don't see why you would need to wrap anything in these ReuseSomeContainer components.
As I understand the question, you have the component SomeContainer which expects some props (values and functions) from redux. You want those values and functions to be different in different parts of your app, but SomeContainer would do the same thing everywhere.
You would just export SomeContainer and the use it in different locations.
Component A:
const ConnectedContainerA = connect(mapStateToPropsA, mapDispatchToPropsA)(SomeContainer);
Component B:
const ConnectedContainerB = connect(mapStateToPropsB, mapDispatchToPropsB)(SomeContainer);

How can I get ownProps using reselect on redux?

I want to create a selector with memoization using reselect based on some ownProps of mapStateToProps.
You can do this by connecting the selector to a component using the connect method provided by react-redux, then passing the component props (ownProps) as the second argument to the selector.
container.js
import { connect } from 'react-redux';
import { getVisibleTodos } from './selectors';
...
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props),
};
};
const VisibleTodoList = connect(
mapStateToProps,
)(TodoList);
export default VisibleTodoList;
You can then access those props in your selector
selectors.js
import { createSelector } from 'reselect';
const getVisibilityFilter = (state, props) =>
state.todoLists[props.listId].visibilityFilter;
const getTodos = (state, props) =>
state.todoLists[props.listId].todos;
const getVisibleTodos = createSelector(
...
);
export default getVisibleTodos;
However, this will not memoize correctly if you have multiple instances of the component you're passing props from. In that case, the selector would receive a different props argument each time, so it would always recompute instead of returning a cached value.
To share a selector across multiple components while passing in props and retaining memoization, each instance of the component needs its own private copy of the selector.
You can do this by creating a function that returns a new copy of the selector each time it's called.
selectors.js
import { createSelector } from 'reselect';
const getVisibilityFilter = (state, props) =>
state.todoLists[props.listId].visibilityFilter;
const getTodos = (state, props) =>
state.todoLists[props.listId].todos;
const makeGetVisibleTodos = () => {
return createSelector(
...
);
}
export default makeGetVisibleTodos;
If the mapStateToProps argument supplied to connect returns a function instead of an object, it will be used to create an individual mapStateToProps function for each instance of the container.
With that in mind, you can create a function makeMapStateToProps that creates a new getVisibleTodos selector, and returns a mapStateToProps function that has exclusive access to the new selector:
import { connect } from 'react-redux';
import { makeGetVisibleTodos } from './selectors';
...
const makeMapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos();
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props),
};
};
return mapStateToProps;
};
const VisibleTodoList = connect(
makeMapStateToProps,
)(TodoList);
export default VisibleTodoList;
Now each instance of the VisibleTodosList container will get its own mapStateToProps function with a private getVisibleTodos selector. Memoization will now work correctly regardless of the render order of the containers.
This was adapted (blatently copied) from the Reselect documentation

Resources