Re-rendering component when props updates with createStructuredSelector - reactjs

I will try and make this as short and sweet as possible while being detailed and descriptive. When my application initializes or loads for the first time, it makes an api call to fetch some blog data. Here is what that looks like more or less:
As you can see the api successfully returns the blogPosts. I then reference these blogPosts in another component using createStructuredSelector from the reselect library. Here is the code:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import VisionBlogItem from '../../components/VisionBlogItem';
import { getBlogItem, getFollowingBlogItems } from './selectors';
export class VisionBlogItemPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props, context) {
super(props, context);
this.state = {
};
}
render() {
return (
<div>
{this.props.blogItem && <VisionBlogItem blog={this.props.blogItem} blogItems={this.props.blogItems} />}
</div>
);
}
}
VisionBlogItemPage.propTypes = {
dispatch: PropTypes.func.isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}),
blogItem: PropTypes.object,
blogItems: PropTypes.array,
};
VisionBlogItemPage.defaultProps = {
blogItem: {},
blogItems: [],
};
const mapStateToProps = (state, ownProps) => {
return createStructuredSelector({
blogItem: getBlogItem(ownProps.location.pathname.match(/([^/]*)\/*$/)[1]),
blogItems: getFollowingBlogItems(ownProps.location.pathname.match(/([^/]*)\/*$/)[1])
});
};
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(
withConnect,
)(VisionBlogItemPage);
What these selector methods are doing, getBlogItem and getFollowingBlogItems, are checking the props location pathname end slug and using it to iterate and array to find the needed blog in the Redux State Store. They then get set to the props named blogItem and blogItems to be passed to the presentational component to render. That presentational component looks something like this:
At the bottom of this presentational component are some links that will change the slug name. So if I'm at localhost:3000/blogPosts/blog-post-0 and I clicked on one of the three blog posts at the bottom, it would then change the url to localhost:3000/blogPosts/blog-post-1 or blog-post-2, blog-post-3, etc. These blog posts at the bottom are links from react-router-dom and the code is written as such:
<Link to={props.blogItems[2].fields.slug} style={styles.seeMoreLink}>
<img src={props.blogItems[2].fields.articleImage.fields.file.url} style={styles.seeMoreImg} alt="" key="2" />
</Link>
So the idea is that it changes the slug of the url and renders the appropriate blog post. However, this is not what is occurring. The url slug does change and I can even see in my container component that the props update and change as well. The location pathname changes from blog-post-1 to blog-post-2. My question then lies why does my component not re-render? I am aware of the life-cycle method of componentWillReceiveProps, but I do not know how to use it in conjunction with something like createStructuredSelector from reselect. Anyone who could possibly give me a clear path as to how I am suppose to handle this it would be greatly appreciated!

Related

Is it possible to use redux and axios in react component library?

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.

Does Redux update the React component it is connected to automatically?

I was under the impression that when my Redux store gets updated (via dispatching an action to the reducer), my Provider should make the new store available to all it's child components. So when I connect a container component to the store by using mapStateToProps(), that component should re-render when it receives the new props that go along with the store being updated, without the need for componentWillReceiveProps(). Am I wrong in thinking that?
After several hours of reading docs and other stack overflow answers, something just isn't clicking for me. I'm pretty sure my reducer is working correctly and is returning a new object, my components have access to the store, and my initial state is rendering just fine. If anyone could give me a better idea about the flow of things, I would be forever grateful.
Basically, I have a header component that imports the store, uses Provider and renders a "FAQ" component:
import React from 'react';
import FAQ from './Faq';
import {Provider} from "react-redux";
import store from '../store/index'
class Header extends React.Component {
render() {
return (
<Provider store = {store}>
<FAQ />
</Provider>
)
}
}
export default Header;
The "FAQ" component is a container that is connected to the store via mapStateToProps(), it imports the "FAQTest" component, which is a presentational component that will get passed this.state.thisFood as props so that it can render the data from the store. There is also an "addFood" function that dispatches my action, which can be seen in mapDispatchToProps() at the bottom.
import React from 'react';
import {connect} from 'react-redux';
import FAQTest from './faqTest';
class FAQ extends React.Component {
constructor(props) {
super(props);
this.state = {
thisFood: props.breakfast
};
}
//adding this makes the component state update, but I think it should work without it
componentWillReceiveProps(nextProps){
this.setState({thisFood: nextProps.breakfast})
}
addFood = () => {
this.props.addFood();
}
render() {
return (
<React.Fragment>
<button onClick={this.addFood}> Add Food </button>
<FAQTest food = {this.state.thisFood} />
</React.Fragment>
)
}
}
const mapStateToProps = function(state) {
return {
breakfast: state.faq.breakfast
}
}
function mapDispatchToProps(dispatch) {
return {
addFood: () => dispatch({type: 'ADD_FOOD', food: 'Waffles'})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(FAQ);
When I click the "Add Food" button, my store gets updated, and the props of my FAQ container component get updated, because of mapStateToProps(). I thought this would trigger my FAQ component to update its state, however the state does not get updated unless I use componentWillReceiveProps. Is this working as expected?
Just in case I'm doing something silly, here is my reducer:
const initialState = {
breakfast: ["eggs", "bacon"]
}
export default function faqReducer(state = initialState, action) {
switch (action.type) {
case "ADD_FOOD":
return Object.assign({}, state, {
breakfast: [...state.breakfast, action.food]
})
default:
return state;
}
}
Here is my root reducer with my combineReducers() function:
import { combineReducers } from "redux";
import faq from './faqReducer'
export default combineReducers({
faq: faq
});
The problem is that you're copying data from props to state, but only doing that when the component is mounted, and then expecting the state to somehow be updated when new props arrive.
Copying data from props to state is almost always the wrong approach. Please don't do that. Just use the value from props.
Two additional suggestions for improving the code:
Prefer using the "object shorthand" form of mapDispatch, rather than writing it as a function
We recommend using our new official Redux Starter Kit package as the standard way to write your Redux logic. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once.

use react-redux with connect() and {...this.props}

I cannot figure out, how to make right solution, when I want to call action in my container from other component, by the way I want to use spread operator because I need to pass too many parametrs in my component and don't want describe all of them.
I know I can pass all props from redux store via props, like this example in Menu, but my component too nested, and I have to send props in eighter component in nest
render() {
return (
<div className="wrapper">
<Menu {...this.props} />
</div>
);
}
}
const mapStateToProps = reduxStore => (
{
app: reduxStore.app
}),
mapDispatchToProps = dispatch => ({appActions: bindActionCreators(appActions, dispatch)});
export default connect(mapStateToProps, mapDispatchToProps)(App);
So, I decided to connect my nested component with redux store, because I need to work from my nested component with store and actions in main container component. But this solution doesn't work, because i use spread operator to my nested component.
render() {
return <Link activeClassName='active' onClick={this.props.appActions.closeMenu} {...this.props} />;
}
And using spread operator is really important because component get too much different parameters from its parent component, and if i don't use {...this.props}, I have to write like this:
render() {
const { to, onlyActiveOnIndex, className, specialIcons } = this.props;
return <Link activeClassName='active' onClick={this.props.appActions.closeMenu} to={to} specialIcons={specialIcons} onlyActiveOnIndex={onlyActiveOnIndex} className={className} >{this.props.children}</Link>;
}
But also, I have to connect to common redux store, and when I connected, occurs an Error, because of my component use {...this.props} and it get all props, including actions from container and component doesn't know what do with them. I find one solution of this proplem, but I'm not sure that it is right variant. I clone props with spread operators, but delete property that contain new functions (actions) from common store.
render() {
let oldProps = {...this.props};
delete oldProps.appActions;
delete oldProps.app;
return <Link activeClassName='active' onClick={this.props.appActions.closeMenu} {...oldProps} >{this.props.children}</Link>;
}
}
const mapState = reduxStore => ({app: reduxStore.app}),
mapDispatchToProps = dispatch => ({appActions: bindActionCreators(appActions, dispatch)});
export default connect(mapState, mapDispatchToProps)(NavLink);
I'm guessing that I don't understand something basic and global in react-redux or I use bad practice. May be I should use higher order components in React? but now I don't know how to make it better.
Here is a functional example. I made it for a personal project. I removed the useless code for the purpose of the example.
Something you might want to get is eslint, it will show you basic mistake people are making while coding.
For example, it will say that you having declared your PropTypes. In your code, where does it say what app is? Sure it's coming from reduxStore.app but what kind of PropTypes is it?
Also, you shouldn't link all the reduxStore state to your component. You should just import what you really need. In my example, I import only users from state.app.users. If I had more, or want all elements of the reducer state, I would import all of them individually and then declare the props like this:
Home.propTypes = {
users: PropTypes.object.isRequired,
actions: {
load: PropTypes.func.isRequired,
},
};
Because JavaScript isn't a typed language, the PropTypes like above help you make typed validation. You can also see the props actions which contains all the functions you import in AppActions in your case.
To see how to use the function from the action afterward, look at my componentWillMount()
import React, { Component, PropTypes } from 'react';
import { ListView} from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as app from '../../actions/appActions';
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
class Home extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: ds.cloneWithRows(props.users.toJS()),
};
}
componentWillMount() {
this.props.actions.load();
}
componentWillReceiveProps(nextProps) {
if (this.props.users !== nextProps.users) {
this.setState({
dataSource: ds.cloneWithRows(nextProps.users),
});
}
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
enableEmptySections
renderRow={
(rowData) => <User haveLunch={rowData.haveLunch} name={rowData.name} />
}
/>
);
}
}
Home.propTypes = {
users: PropTypes.object.isRequired,
actions: {
load: PropTypes.func.isRequired,
},
};
function mapStateToProps(state) {
return {
users: state.app.users,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(app, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Hope this will help ya ;)

Wondering where to house api call logic. inside the Container or Component?

I have a container that passes props and an apiCall action to a component which will mainly just render the result of that call. My question is should I leave the invoking of that action up to the component or move it out into the container and just pass the array of items to the component?
Here is my container code. The fetchShowingsListShowings is the one in question. Also, I will be renaming that soon enough so bear with me.
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as actions from '../actions/showingsListActions';
import ShowingsList from '../components/ShowingsList';
const ShowingsListContainer = (props) => {
return (
<ShowingsList
isLoading={props.isLoading}
showings={props.showings}
fetchShowingsListShowings={props.actions.fetchShowingsListShowings}
/>
);
};
ShowingsListContainer.propTypes = {
isLoading: PropTypes.bool.isRequired,
showings: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
const mapStateToProps = (state) => {
return {
isLoading: state.showingsList.isLoading,
showings: state.showingsList.showings
};
};
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ShowingsListContainer);
And my component. Which calls the API action on componentWillMount.
import React, { PropTypes } from 'react';
import ShowingsListItem from './ShowingsListItem';
class ShowingsList extends React.Component {
componentWillMount() {
this.props.fetchShowingsListShowings();
}
render() {
return (
this.props.isLoading ? <h1>Loading...</h1> :
<ul className="list-unstyled">
{this.props.showings.map((showing,index) => <ShowingsListItem showing={showing} key={'showing' + index}/>)}
</ul>
);
}
}
ShowingsList.propTypes = {
isLoading: PropTypes.bool.isRequired,
showings: PropTypes.array.isRequired,
fetchShowingsListShowings: PropTypes.func.isRequired
};
export default ShowingsList;
Thanks in advance.
So in React with Redux the term 'Container' just means a component that is connected to the Store, essentially whatever you use the react-redux 'connect' method with. Your ShowingsList can be a 'dumb' (or functional) component meaning it's just a component that takes in data and displays content. The general 'best' practice is to have your dumb components just be concerned with presentation, and your container components handle all the logic interacting with the Redux Store. If you follow this logic, fetch the data in the container, and pass the data to the nested component. That being said, it'll work either way so you don't really need to change anything if you're happy with it now.
To follow this pattern do something like this:
modify your Container component to be an ES6 class extends React.Component.. and optionally change your ShowingsList to be a functional component (like your ShowingsList is now)
put a componentWillMount in your Container and put the API call there.
pass the list to the presentational component.
Here's an article written by Dan Abramov, the author of Redux on this very topic.
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.g695y2gwd

React-Redux - Reuseable Container/Connector

I am completely lost on the react-redux container (ie connector) concept as it is not doing what I anticipated. My issue is straight forward, and to me reasonable, yet I cannot find a well written example of how to accomplish it.
Let us say we have a react component that connects to a store that has product context, and we will call this component ProductContext.
Furthermore, let's say we want to reuse ProductContext liberally throughout the app so as to avoid the boilerplate code of dispatching actions on every other component that may need products.
Illustratively this is what I mean:
from DiscountuedProducts:
<ProductContext >
// do something with props from container
</ProductContext >
from SeasonalProducts:
<ProductContext >
// do something with props from container
</ProductContext >
From the examples I see at react-redux, it appears to me that their containers lump both seasonal and discontinued products in the container itself. How is that reusable?
from the ProductContextComponent:
<section >
<DiscontinuedProducts />
<SeasonalProducts />
</section >
Complicating matters, while trying to keep a cool head about this most frustrating matter, "nebulous tersity" seems to be the only responses I receive.
So here is my ProductContext:
#connect(state => ({
products: state.productsReducer.products
}))
export default class ProductContext extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const { dispatch } = this.props;
const clientId = this.context.clientInfo._id;
dispatch(fetchProductsIfNeeded(clientId));
}
// get from parent
static contextTypes = {
clientInfo: PropTypes.object.isRequired
};
static propTypes = {
children: PropTypes.node.isRequired,
dispatch: PropTypes.func.isRequired,
products: PropTypes.array
};
render() {
if (!this.props.products) return <Plugins.Loading />;
.....
return (
// I want to put the products array here
);
}
};
Then my thinking is if I do this:
from DiscountuedProducts:
<ProductContext >
// do something with props from container
</ProductContext >
DiscontinuedProducts should have knowledge of those products and it can simply filter for what is discontinued.
Am I completely wrong on this? Is this not reasonable to desire?
If anyone knows of a exhaustive example on the Net demonstrating how this can be achieved, I would much appreciate it being pointed out to me. I have spent over a week on this issue and am about ready to give up on react-redux.
UPDATE: A very slick solution below with use of a HOC.
If anyone knows of a exhaustive example on the Net demonstrating how this can be achieved, I would much appreciate it being pointed out to me.
Look at the Shopping Cart example.
Examples code from: shopping-cart/containers/CartContainer.js
Let us say we have a react component that connects to a store that has product context,
There isn't a store for products and a store for users, etc. There is one store for everything. You should use reducers to take the full store and reduce to what your component needs.
From the example:
import { getTotal, getCartProducts } from '../reducers'
const mapStateToProps = (state) => {
return {
products: getCartProducts(state),
total: getTotal(state)
}
}
Here state is the entire store.
let's say we want to reuse ProductContext liberally throughout the app so as to avoid the boilerplate code of dispatching actions
Components don't dispatch actions, they call functions passed to them. The functions live in an actions module, which you import and pass to container as props. In turn, the container passes those functions to component props.
From the example:
import { checkout } from '../actions'
CartContainer.propTypes = {
checkout: PropTypes.func.isRequired
}
connect(mapStateToProps,
{ checkout }
)(CartContainer)
What connect does, it subscribes to store changes, calls the map function, merges with constant props (here the action function), and assigns new props to the container.
DiscontinuedProducts should have knowledge of those products and it can simply filter for what is discontinued
This is actually spot on. The knowledge you mention is the one store, and it should absolutely filter in a reducer.
Hopefully this clears things up.
I have found a more practical way of utilizing repetitive data from redux than the docs. It makes no sense to me to repeat mapToProps and dispatch instantiation on every blessed component when it was already done once at a higher level, and there inlies the solution. Make sure your app is Babel 6 compliant as I used decorators.
1. I created a higher order component for the context ....
product-context.js:
import React, { Component, PropTypes } from 'react';
// redux
import { connect } from 'react-redux';
import { fetchProductsIfNeeded } from '../../redux/actions/products-actions';
// productsReducer is already in state from index.js w/configureStore
#connect(state => ({
products: state.productsReducer.products
}))
export default function ProductContext(Comp) {
return (
class extends Component {
static propTypes = {
children: PropTypes.node,
dispatch: PropTypes.func.isRequired,
products: PropTypes.array
};
static contextTypes = {
clientInfo: PropTypes.object.isRequired;
};
componentDidMount() {
const { dispatch } = this.props;
const clientId = this.context.clientInfo._id;
dispatch(fetchProductsIfNeeded(clientId));
}
render() {
if (!this.props.products) return (<div>Loading products ..</div>);
return (
<Comp products={ this.props.products }>
{ this.props.children }
</Comp>
)
}
}
)
}
2. component utilizing product-context.js
carousel-slider.js:
import React, { Component, PropTypes } from 'react';
......
import ProductContext from '../../../context/product-context';
#Radium
#ProductContext
export default class CarouselSlider extends Component {
constructor(props) {
super(props); }
......
static showSlideShow(carouselSlides) {
carouselSlides.map((slide, index) => {
......
results.push (
......
)
});
return results;
}
render() {
const carouselSlides = this.props.products;
const results = CarouselSlider.showSlideShow(carouselSlides);
return (
<div id="Carousel" className="animation" ref="Carousel">
{ results }
</div>
);
}
}
So there you go. All I needed was a decorator reference to product-context, and as a HOC, it returns the carousel component back with the products prop.
I saved myself at least 10 lines of repetitive code and I removed all related contextType from lower components as it is no longer needed with use of the decorator.
Hope this real world example helps as I detest todo examples.

Resources