Where to put withRouter in react - reactjs

In my application, I seperated redux part from the react components. Now i'm confused with, where to put the withRouter(). It works on both component and container, but what will be the good practice?
Here is my code.
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import {bindActionCreators} from 'redux';
import RegisterScreen from './RegisterScreen';
import { registerUser } from '../../../actions';
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
const mapDispatchToProps = dispatch => ({
registerUser: bindActionCreators(registerUser, dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(RegisterScreen));
Here if we export like, this and set withRouter in the component, still it works. What is the better approach?
export default connect(mapStateToProps, mapDispatchToProps)(RegisterScreen)
enter code here

IMO it would be cleaner to do it in the component( v.s. its container, if it has one).
That way there can be components that are not connected to the global state(Do not have a container) which still can be wrapped with withRouter if they need its functionality.

As far as I know, the only time where it matters is when you want to access the router in ownProps in mapStateToProps.
You need to wrap the connected component in order to access the router in mapStateToProps, like this:
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps,
)(RegisterScreen));

Related

Testing React.js Container components with React testing library

I have container components that all look similar to this:
import { connect } from 'react-redux';
import testAction from '../actions/TestComponentActions';
import TestComponent from '../components/TestComponent';
const mapDispatchToProps = {
testAction,
};
const mapStateToProps = (state: any) => state.testInformation;
export const TestContainer: any = connect(mapStateToProps, mapDispatchToProps)(TestComponent);
export default TestContainer;
how can I write unit tests for this component? Other than the simple render() and expect().toBeTruthy();
is that all that needs to be done? or can I get a little more specific here. thank you in advance

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?

Dispatch is not available in this.props

I'm very new to React and trying to write an application which outputs a portfolio to one part of the page and, based on user interaction with that portfolio, displays some information in a lightbox/modal elsewhere in the DOM.
This requires that my two rendered components have some kind of shared state, and my understanding is that the best (or one of the best) way to achieve this is with Redux. However, being new to React and now adding Redux into the mix, I'm a little out of my depth.
I've created some (for now very dumb) action creators and reducers, all I'm trying to do initially is fetch some JSON and add it to my store. However, I'm not able to access dispatch from within my component and I'm not really sure where I'm going wrong.
If I console.log this.props from within my component I get an empty object, "{}".
Here are the main parts, any pointers would be really appreciated:
App.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import store from './redux/store';
import { Portfolio } from './redux/components/portfolio';
ReactDOM.render(
<Provider store={store}>
<Portfolio />
</Provider>,
document.getElementById('portfolioCollection')
);
actions/actionCreators.js:
export const populatePortfolio = obj => ({
type: POPULATE_PORTFOLIO,
obj
});
export const populateLightbox = obj => ({
type: POPULATE_LIGHTBOX,
obj
});
portfolio.js:
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
export class Portfolio extends React.Component {
componentDidMount() {
this.getPortfolioData();
}
getPortfolioData() {
fetch('/data.json')
.then( (response) => {
return response.json()
})
.then( (json) => {
// dispatch action to update portfolio here
});
}
render() {
return(
// render component
);
}
}
function mapStateToProps(state){
console.log('state', state);
return {
state: state
}
};
function mapDispatchToProps(dispatch) {
console.log('dispatch', dispatch);
return {
actions: bindActionCreators({ populatePortfolio: populatePortfolio }, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Portfolio);
this.props is empty because you have not passed any props. You are using the unconnected component instead of the one that has been connected to redux.
To fix this, replace this line:
import { Portfolio } from './redux/components/portfolio';
with
import Portfolio from './redux/components/portfolio';
You are exporting both the connected and the unconnected component. You probably only want the last export. Since the connected component is exported as default you import it without using {} deconstruction.
unless you need to import the unconnected component in tests or something like that, you can remove the export statement from this line, since it makes no sense to export something that you don't intend to import in another file.
export class Portfolio extends React.Component {
You aren't meant to manually call dispatch in your components. The action creator function is automatically bound to dispatch for you. Simply call this.props.populatePortfolio() in your component.

Dispatching an action from a Redux container without extending React.Component

I have a container component within my React and Redux application:
import { connect } from 'react-redux'
import MyComponent from '../components/mycomponent'
const mapStateToProps = state => ({
myData: state.myData[state.activeDataId]
})
export default connect(mapStateToProps)(MyComponent)
If state.myData[state.activeDataId] does not exist then I want to dispatch an action to fetchMyData or fetchMyDataIfNeeded.
Note that, at the moment, my container does not contain any JSX, it just forwards props to a presentational component. I have seen this being called a 'Pure Container' though I'm not sure if that's a common term.
Is there a common pattern to dispatch actions from a Pure Container? I am thinking without:
expecting the presentational component to worry about this logic by passing an onLoad event to it
making the container a React.Component and triggering via componentDidMount
Is it a bad idea to dispatch actions from mapStateToProps, mapDispatchToProps or mergeProps?
As noted elsewhere, doing this in the container is a bad idea.
Instead of worrying about this in the container, it makes sense to fetch the data conditionally in your component. I know you mentioned not wanting to extend react.component, but you should definitely consider making this component a class in order to fetch data in a component lifecycle hook.
As detailed in another answer, connect takes a second argument of mapDispatchToProps. Pass in the fetchData dispatcher there (example of how to do this here.)
Then, in your component you can check myData. If it is not there, then you dispatch via
this.props.whatYouCalledDispatch()
Yes, it is a bad idea to dispatch any action in container.
In your case, the best approach is:
Map your state, action creator to component props
Check the props in componentDidMount (or componentDidUpdate) and fetchDataYouNeed, then component will be updated
Your container should be:
import { connect } from 'react-redux';
import {fetchDataYouNeed} from './actions
import MyComponent from '../components/mycomponent';
const mapStateToProps = state => ({
myData: state.myData[state.activeDataId]
});
const mapDispatchToProps = (dispatch) => {
return {
fetchDataYouNeed: ()=>{
dispatch(fetchDataYouNeed());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
Your component
class YourComponent extends Component{
componentDidMount(){
let {myData, activeDataId} = this.props;
if(myData && !myData[activeDataId]){
this.props.fetchDataYouNeed();
}
}
render(){
....
}
}
Learn more here https://facebook.github.io/react/docs/react-component.html#componentdidmount
This seems to work, though I'm not sure if it has any unintended effects:
import { connect } from 'react-redux'
import MyComponent from '../components/mycomponent'
import { fetchMyData } from '../actions/mydata'
const mapStateToProps = state => ({
dataId: state.activeDataId,
myData: state.myData[state.activeDataId]
})
const mapDispatchToProps = { fetchMyData }
const mergeProps = (stateProps, dispatchProps) => {
if (!stateProps.myData) {
dispatchProps.fetchMyData(stateProps.dataId)
}
return stateProps
}
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)
Alternatively, brianzinn suggested that by using Redux Saga to manage side effects, this issue becomes redundant.

How to use multiple multiActions in react/redux?

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);

Resources