Correct way to pre-load component data in react+redux - reactjs

I do not know the correct way to pre-load data from API for a component to use.
I have written a stateless component which should render the data:
import React, { PropTypes } from 'react';
const DepartmentsList = ({ departments }) => {
const listItems = departments.map((department) => (
<li>{department.title}</li>
));
return (
<ul>
{listItems}
</ul>
);
};
DepartmentsList.propTypes = {
departments: PropTypes.array.isRequired
};
export default DepartmentsList;
And I have an action which will retreive data from the API:
import { getDepartments } from '../api/timetable';
export const REQUEST_DEPARTMENTS = 'REQUEST_DEPARTMENTS';
export const RECEIVE_DEPARTMENTS = 'RECEIVE_DEPARTMENTS';
const requestDepartments = () => ({ type: REQUEST_DEPARTMENTS });
const receiveDepartments = (departments) => ({ type: RECEIVE_DEPARTMENTS, departments });
export function fetchDepartments() {
return dispatch => {
dispatch(requestDepartments);
getDepartments()
.then(departments => dispatch(
receiveDepartments(departments)
))
.catch(console.log);
};
}
Now I think I have a few options to preload departments that are required for the list. I could use redux-thunk and mapDispatchToProps to inject fetchDepartments to the stateless component and implement componentWillMount or similar lifecycle method, to load data - but then I don't need to pass the list via props, as the component would always load data for himself, and I don't want that, because whenever a new component is created the data is fetched from api instead of store...
Another advice I've seen is to use getComponent function from react-router, and retreive all data before returning the component, however, I am not sure if it's the correct redux way, as I don't see how to use redux-thunk there, and logic kind of seems littered all accross the files, when it's the data required for only one component.
This leaves me with the only seemingly ok option to load data in container component's lifecycle methods, but I want to know what is considered the best practice for what I want to do.

The most 'redux-like' way of handling the pre-loading of data would be to fire off the asynchronous action in the lifecycle method (probably componentWillMount) of a Higher Order Component that wraps your app. However, you will not use the results of the API call directly in that component - it needs to be handled with a reducer that puts it into your app store. This will require you to use some sort of a thunk middleware to handle the asynchronous action. Then you will use mapStateToProps to simply pass it down to the component that renders the data.
Higher Order Component:
const mapStateToProps = (state) => {
return {
departments: state.departments
};
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
getDepartments: actionCreators.fetchDepartments
});
}
class App extends Component {
componentWillMount() {
this.props.getDepartments();
}
render() {
return <DepartmentsList departments={this.props.departments} />
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
reducers:
export function departments(state = [], action) {
switch(action.type) {
case 'RECEIVE_DEPARTMENTS':
return action.departments;
}
}

Related

Should I use ComponentDidMount or mergeProps of connect function for data fetching?

I use react with redux and have a component which displays some dataSet fetched from external source. My current code looks like:
const ConnectedComponent = connect(
state => ({
dataSet: state.dataSet
}),
dispatch => ({
loadData: () => {
...
fetch data and dispatch it to the store
...
}
})
)(MyComponent);
class MyComponent extends Component {
...
componentDidMount() {
const { dataSet, loadData } = this.props;
if (!dataSet) {
loadData();
}
}
...
render () {
const { dataSet } = this.props;
if (dataSet) {
// render grid with data
} else {
// render Loading...
}
}
}
The code above works but I'm wondering would it be better to get rid of componentDidMount and just check for data and load it from within connect function? The code might looks like:
const ConnectedComponent = connect(
state => ({
dataSet: state.dataSet
}),
dispatch => ({
dispatch
}),
(stateProps, dispatchProps) => {
const { dataSet } = stateProps;
const { dispatch } = dispatchProps;
if (!dataSet) {
// fetch data asynchronously and dispatch it to the store
}
return {
...stateProps
};
}
)(MyComponent);
class MyComponent extends Component {
render () {
const { dataSet } = this.props;
if (dataSet) {
// render grid with data
} else {
// render Loading...
}
}
}
The latter code looks more attractive to me because of MyComponent becomes simpler. There is no passing of execution first forward from connected component to presentational and then backward, when componentDidMount detects that there are no data ready to display.
Are there some drawbacks of such approach?
PS: I use redux-thunk for an asynchronous fetching.
The second approach, as separation of concepts, is potentially a good solution, because of the layers and responsibility separations - ConnectedComponent is responsible for data fetching, while MyComponent acts as presentational component. Good!
But, dispatching actions in connect mergeProps doesn't seem a good idea, because you introduce side effects.
Also, other drawback I'm seeing, is that the flow of fetching and returning data would be repeated across your different pages (components). Generally speaking the following flow would be repeated:
Connected components call the API for the needed Entities.
While fetching the Entities, we’re showing a Loader.
When the data is available, we pass it to the Presentational components.
Because of the above drawbacks, I can suggest you to organize and reuse your data fetching flow in a HOC.
Here's a pseudo code and flow (taken from my article) that addresses the above drawbacks:
* I've been using it last 1 year and continue stick with it.
Fetcher HOC:
import authorActions from 'actions/author'
const actions = {
'Author': authorActions
}
export default (WrappedComponent, entities) => {
class Fetcher extends React.Component {
// #1. Calls the API for the needed Entities.
componentDidMount () {
this.fetch()
}
fetch () {
const { dispatch } = this.props
entities.forEach(name => dispatch(actions[name].get()))
}
render () {
const { isFetching } = this.props
// #2. While fetching the Entities, we're showing an Loader.
if (isFetching) return <Loader />
return <WrappedComponent {...this.props} />
}
}
const mapStateToProps = state => {
const isFetching = entities
.map(entity => state[entity].isFetching)
.filter(isFetching => isFetching)
return { isFetching: isFetching.length > 0 }
}
return connect(mapStateToProps)(Fetcher)
}
Usage:
const MyComponent = ({ authors }) => <AuthorsList authors={authors} />
const mapStateToProps = state => ({
authors: state.authors
})
const Component = connect(mapStateToProps)(MyComponent)
export default Fetcher(Component, ['Author'])
Here you can read the article and deep dive into its ideas and concepts:
* Fetcher concept is addressed at Lesson #2: Containers on steroids
Long-term React & Redux SPA — Lessons learned

Is this Flux architecture?

This is how I've been organizing my React / Redux projects because it's how they did it in the tutorial I followed. Is this what Flux architecture is and if not what would you call this?
First I call a function in my component that's defined in the action file
This function does an ajax request to get info from an API
Then it fires off an action creator
The reducer listens for action creators and once one is detected it executes a function that updates the state
Here's an example:
Component
class List extends React.Component {
componentDidMount() {
this.props.getPosts();
}
// etc...
}
const mapStateToProps = state => {
return {
posts: state.posts
};
};
const mapDispatchToProps = dispatch => {
return {
getPosts: () => dispatch(actions.getPosts())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(List);
Action
const postsLoaded = posts => {
return {
type: actionTypes.POSTS_LOADED,
posts: posts
};
};
export const getPosts = () => {
return dispatch => {
axios
.get('http://api.something.com/posts', {})
.then(response => {
dispatch(postsLoaded(response.posts));
})
.catch(e => {
console.error(e);
});
};
};
Reducer
const setPosts = (prevState, action) => {
return {
...prevState,
...action.posts
};
};
const reducer = (prevState = {}, action) => {
switch (action.type) {
case actionTypes.POSTS_LOADED:
return setPosts(prevState, action);
default:
return prevState;
}
};
export default reducer;
Flux is a design pattern. Redux is one of several libraries that implement Flux. The intent is NOT for you to "use Redux to implement Flux", but rather "use the Flux pattern by using Redux".
You can find a much better description in the docs below, but in simplest terms, the Flux architecture is based on a unidirectional data flow, which means that each piece receives data from one place, and outputs changes to another. The intent of this pattern is to eliminate "spaghetti code", where various parts of the application pass data in many different directions, which can eventually become very difficult to trace.
In other words, your components are the "View" in the diagram below.
Redux store gives state to your component
Your component renders something, and when a user performs an action, the component creates an action and gives it to the dispatcher.
The dispatcher finds the reducer that can handle your action, and gives the result to the store.
And the cycle repeats.
This image and an in-depth overview of Flux can be found here.

right way to POST data to a server and handle response with redux

I'm very new to react and redux.
Now I want to rewrite my post request with a redux process.
my current request looks like this:
_handleSubmit(event) {
axios
.post('/createUrl', {
url: this.state.url
})
.then((response) => {
this.setState({
shortenInfos: response.data
})
})
.catch((error) => {
console.log(error);
});
event.preventDefault()
}
now I created a store:
export default function url(state = 0, action) {
switch (action.type) {
case 'CREATE_URL':
// maybe axios request?!
return `${action.url}/test`
case 'CREATED_URL':
return `${action.url}/created`
default:
return state
}
}
so where I must use my store.dispatch()? Should I make my _handleSubmit something like this?
_handleSubmit(event) {
axios
.post('/createUrl', {
url: this.state.url
})
.then((response) => {
store.dispatch({
type: 'CREATED_URL',
url: response.data
})
})
.catch((error) => {
console.log(error);
});
event.preventDefault()
}
I think this is wrong? And where I must use mapStateToProps method? Or should I do the axios-request in my CREATE_URL in my reducer?
Introduction
Using React with Redux gives you high freedom on how you can do things. The downside of this is that it can be hard to find out how things should be done properly, mainly because there is no standard or comprehensive guide to the use of the many dependency you need for a properly implemented project. This answer will guide you through the basics with links to references that will help you to find out wheres next and how to deeper your knowledge.
Reducer
Reducers should be pure, meaning that they have no side effects (like making axios requests) and they should always return a new object/array/value instead of changing the previous state. It is also a good practice to use action types as constants. You can place action types wherever you want, but for simplicity I will put them into the reducer's file, but there are better ways to organize them like using ducks.
export const CREATED_URL = 'CREATE_URL';
export default const url = (state = '', action) => {
switch (action.type) {
case CREATED_URL:
return action.url;
default:
return state;
}
};
Asynchronous actions
Everything that causes a side effect should be an action, so XHR should happen there. Because XHR should be asynchronous it is recommended to use a middleware: redux-thunk and redux-saga are two popular solutions. I will go with thunk so install it first.
First (because const has temporal dead zone) you need an action that will "load" the result of the XHR to the store:
import { CREATED_URL } from './reducer';
const createdUrl = url => ({
type: CREATED_URL,
url, // ES6 trailing comma for prettier git diffs
});
Then you can create the action that will fire the XHR, wait for the response then load it to the store using the action created previously. We need to return a function that will receive dispatch as the parameter. This technique is used in functional programming and is called currying.
export const createUrl = url => dispatch => { // with only 1 parameter the parentheses can be omited
axios
.post('/createUrl', { url }) // ES6 Shorthand property name in { url }
.then(response => {
dispatch(createdUrl({
url: response.data,
})
})
.catch(error => {
// #TODO dispatch an action that will show a message
// notifying the user that the request failed
console.log(error);
});
}
Usage in the React component.
Preparation
For ease of use, you need to connect your React component with Redux. react-redux comes to the rescue. Read the API documentation and add the <Provider> component to the root of your React component tree.
Now, in the top of your React component's file, import all the necessary stuff:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { createUrl } from './reducer';
mapStateToProps and mapDispatchToProps
Then create the two helper functions for connect:
const mapStateToProps = store => ({ url: store.url })
const mapDispatchToProps = dispatch => bindActionCreators({ createUrl }, dispatch)
With the help of mapStateToProps you can subscribe to store updates and inject the important parts of the Redux store to your components props. mapStateToProps should return an object that will be merged to the component's props. Usually we just do something like store => store.url but because our example is so simple that the reducer returns a plain string instead of something more complex in an object, we need to wrap that string into an object over here.
mapDispatchToProps with the help of bindActionCreators will inject the passed actions to the component's props so we can call and pass them down to subcomponents with ease: this.props.createUrl().
The component itself
Now we can create the component itself. I will use an ES6 class to show an example with componentDidMount, but if you don't need that and you have a stateless component, you can go with a function too.
class Example extends React.Component {
componentDidMount() {
// If you need to load data from a remote endpoint place the action call here, like so:
// this.props.createUrl('your-url');
}
render() {
return (
<div>
<div>URL injected from the store, automatically refreshed on change: {this.props.url}</div>
<div onClick={event => {this.props.createUrl('your-url');}}>Click me to fetch URL</div>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Example)

How to dispatch Redux action from stateless component when route is loaded?

Goal: when loading a react-router route, dispatch a Redux action requesting asynchronic Saga worker to fetch data for the underlying stateless component of that route.
Problem: stateless components are mere functions and don't have lifecycle methods, such as componentDidMount, so I can't(?) dispatch Redux action from inside the function.
My question is partly related to Converting stateful React component to stateless functional component: How to implement "componentDidMount" kind of functionality? , but my goal is to merely dispatch a single Redux action requesting data to be populated to the store asynchronously (I use Saga, but I think that's irrelevant to the problem, as my goal is to merely dispatch an ordinary Redux action), after which the stateless component would re-render due to the changed data prop.
I am thinking of two approaches: either use some feature of react-router, or Redux's connect method. Is there a so-called "React-way" to accomplish my goal?
EDIT: the only solution I have come up with so far, is dispatching the action inside mapDispatchToProps, this way:
const mapStateToProps = (state, ownProps) => ({
data: state.myReducer.data // data rendered by the stateless component
});
const mapDispatchToProps = (dispatch) => {
// catched by a Saga watcher, and further delivered to a Saga worker that asynchronically fetches data to the store
dispatch({ type: myActionTypes.DATA_GET_REQUEST });
return {};
};
export default connect(mapStateToProps, mapDispatchToProps)(MyStatelessComponent);
However, this seems somehow dirty and not the correct way.
I don't know why you absolutly want a stateless component, while a stateful component with componentDidMount would do the job in a simple way.
Dispatching actions in mapDispatchToProps is very dangerous and may lead to dispatching not only on mount but whenever ownProps or store props changes. Side effects are not expected to be done in this method that should remains pure.
One easy way to keep your component stateless is to wrap it into an HOC (Higher-Order Component) that you could easily create:
MyStatelessComponent = withLifecycleDispatch(dispatch => ({
componentDidMount: function() { dispatch({ type: myActionTypes.DATA_GET_REQUEST })};
}))(MyStatelessComponent)
Note that if you use Redux connect after this HOC, you can easily access dispatch from props directly as if you don't use mapDispatchToProps, dispatch is injected.
You can then do something very simple like:
let MyStatelessComponent = ...
MyStatelessComponent = withLifecycle({
componentDidMount: () => this.props.dispatch({ type: myActionTypes.DATA_GET_REQUEST });
})(MyStatelessComponent)
export default connect(state => ({
date: state.myReducer.data
}))(MyStatelessComponent);
HOC definition:
import { createClass } from 'react';
const withLifeCycle = (spec) => (BaseComponent) => {
return createClass({
...spec,
render() {
return BaseComponent();
}
})
}
Here is a simple implementation of what you could do:
const onMount = (onMountFn) => (Component) => React.createClass({
componentDidMount() {
onMountFn(this.props);
},
render() {
return <Component {...this.props} />
}
});
let Hello = (props) => (
<div>Hello {props.name}</div>
)
Hello = onMount((mountProps) => {
alert("mounting, and props are accessible: name=" + mountProps.name)
})(Hello)
If you use connect around Hello component, they you can inject dispatch as props and use it instead of an alert message.
JsFiddle
Now days you can use the useEffect hook as such:
import React, { useEffect } from 'react';
const MyStatelessComponent: React.FC = (props) => {
useEffect(() => {
props.dispatchSomeAction();
});
return ...
}
This is the equivalent for the componentDidMount/componentWillMount life cycle methods of functional/stateless components.
For further reading on hooks: https://reactjs.org/docs/hooks-intro.html
I think I found the cleanest solution without having to use stateful components:
const onEnterAction = (store, dispatchAction) => {
return (nextState, replace) => {
store.dispatch(dispatchAction());
};
};
const myDataFetchAction = () => ({ type: DATA_GET_REQUEST });
export const Routes = (store) => (
<Route path='/' component={MyStatelessComponent} onEnter={onEnterAction(store, myDataFetchAction)}/>
);
The solution passes the store to a higher order function that is passed to the onEnter lifecycycle method.
Found the solution from https://github.com/reactjs/react-router-redux/issues/319
If you want it to be completely stateless you can dispatch an event when the route is entered using onEnter event.
<Route to='/app' Component={App} onEnter={dispatchAction} />
Now you can write you function here provided you either import dispatch in this file or somehow pass it as parameter.
function dispatchAction(nexState,replace){
//dispatch
}
But this solution I feel is even more dirty.
The other solution which I could be really efficient is using containers and calling componentDidMount in that.
import React,{Component,PropTypes} from 'react'
import {connect} from 'react-redux'
const propTypes = {
//
}
function mapStateToProps(state){
//
}
class ComponentContainer extends Component {
componentDidMount(){
//dispatch action
}
render(){
return(
<Component {...this.props}/> //your dumb/stateless component . Pass data as props
)
}
}
export default connect(mapStateToProps)(ComponentContainer)
In general, I don't think this is possible without some kind of trigger action which is dispatched when the component is mounted/rendered for the first time. You've achieved this by making mapDispatchToProps impure. I 100% agree with Sebastien that this is a bad idea. You could also move the impurity to the render function, which is even worse. The component lifecycle methods are meant for this! His HOC solution makes sense, if you don't want to have to write out the component classes.
I don't have much to add, but in case you just wanted to see the actual saga code, here's some pseudocode, given such a trigger action (untested):
// takes the request, *just a single time*, fetch data, and sets it in state
function* loadDataSaga() {
yield take(myActionTypes.DATA_GET_REQUEST)
const data = yield call(fetchData)
yield put({type: myActionTypes.SET_DATA, data})
}
function* mainSaga() {
yield fork(loadDataSaga);
... do all your other stuff
}
function myReducer(state, action) {
if (action.type === myActionTypes.SET_DATA) {
const newState = _.cloneDeep(state)
newState.whatever.data = action.data
newState.whatever.loading = false
return newState
} else if ( ... ) {
... blah blah
}
return state
}
const MyStatelessComponent = (props) => {
if (props.loading) {
return <Spinner/>
}
return <some stuff here {...props.data} />
}
const mapStateToProps = (state) => state.whatever;
const mapDispatchToProps = (dispatch) => {
// catched by a Saga watcher, and further delivered to a Saga worker that asynchronically fetches data to the store
dispatch({ type: myActionTypes.DATA_GET_REQUEST });
return {};
};
plus the boilerplate:
const sagaMiddleware = createSagaMiddleware();
export default connect(mapStateToProps, mapDispatchToProps)(MyStatelessComponent);
const store = createStore(
myReducer,
{ whatever: {loading: true, data: null} },
applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(mainSaga)

What are selectors in redux?

I am trying to follow this code in redux-saga
export const getUser = (state, login) => state.entities.users[login]
export const getRepo = (state, fullName) => state.entities.repos[fullName]
Which is then used in the saga like this:
import { getUser } from '../reducers/selectors'
// load user unless it is cached
function* loadUser(login, requiredFields) {
const user = yield select(getUser, login)
if (!user || requiredFields.some(key => !user.hasOwnProperty(key))) {
yield call(fetchUser, login)
}
}
This getUser reducer (is it even a reducer) looks very different from what I would normally expect a reducer to look like.
Can anyone explain what a selector is and how getUser is a reducer and how it fits in with redux-saga?
getUser is not a reducer, it is indeed a selector, that is, a function that knows how to extract a specific piece of data from the store.
Selectors provide an additional layer such that if you altered your store structure and all of a sudden your users were no longer at state.entities.users but instead at state.users.objects.entities (or whatever) then you only need to update the getUser selector and not every place in your app where you were making a reference to the old location.
That makes them particularly handy when it comes to refactoring your Redux store.
Selectors are getters for the redux state. Like getters, selectors encapsulate the structure of the state, and are reusable. Selectors can also compute derived properties.
You can write selectors, such as the ones you saw in redux-saga. For example:
const getUsersNumber = ({ users }) => users.length;
const getUsersIds = ({ users }) => users.map(({ id }) => id);
etc...
You can also use reselect, which is a simple “selector” library for Redux, that memoize selectors to make them more efficient.
Selectors are functions that take Redux state as an argument and return some data to pass to the component.
const getUserData = state => state.user.data;
Why should it be used?
One of the main reasons is to avoid duplicated data in Redux.
Your data object shape keeps varying as your application grows, so rather than making changes in all the related component.It is much recommended/easier to change the data at one place.
Selectors should be near reducers because they operate on the same state. It is easier for data to keep in sync.
Using reselect helps to memoize data meaning when the same input is passed to the function, returns the previous result rather than recalculating again.So, this enhances your application performance.
function mapStateToProps (state) {
return {
user: state.user,
}
}
initialState of reducer by user store
const initialState = {
isAdmin:false,
isAuth:false,
access:[1,2,5]
};
class AppComp extends React.Component{
render(){
const {user: { access:access}} = this.props;
const rand = Math.floor(Math.random()*4000)
return (<div>
{`APP ${rand} `}
<input type="button" defaultValue="change auth" onClick={this.onChangeUserAuth} />
<p>TOTAL STATUS COUNT IS {access.length}</p>
</div>)
}
}}
but you can use selector
var getUser = function(state) {
return state.user
}
const getAuthProp = createSelector(
getUser,
(user) => user.access
);
function mapStateToProps (state) {
return {
// user: state.user,
access: getAuthProp(state)
}
}
Main Problem is this component use all user: state.user and any changes in user (etc isAdmin ,isAuth, access) runs rerender this component which need only part of this store - access!!!
In Redux, whenever an action is called anywhere in the application,
all mounted & connected components call their mapStateToProps
function. This is why Reselect is awesome. It will just return the
memoized result if nothing has changed.
In the real world, you will most likely need the same certain part of
your state object in multiple components.
https://medium.com/#parkerdan/react-reselect-and-redux-b34017f8194c
The createSelector function provided by Reselect implements the most basic way to derive a selector from previous selectors. The simplest use case is to derive a selector from a single other selector. In this case, the parameters to createSelector are the input selector and a function transforming the result of that selector into the result of the new selector. For example
var getProducts = function(state) {
return state.products
}
import {getProducts} from '../app/selectors'
import {createSelector} from 'reselect'
export const getProductTitles = createSelector(
getProducts,
(products) => products.map((product) => product.get('title'))
)
This is equivalent to (ignoring memoization):
import {getProducts} from '../app/selectors'
export const getProductTitles = (state) => {
return getProducts(state).map((product) => product.get('title'))
}
The createSelector function can combine data from multiple selectors as well as from a single selector. We can pass any number of selectors to createSelector, and their results will be passed to the function passed as the final argument. For a (somewhat contrived) example:
const isInCheckout = createSelector(
getIsShippingPage,
getIsBillingPage,
getIsConfirmationPage,
(isShipping, isBilling, isConfirmation) =>
isShipping || isBilling || isConfirmation
)
is equivalent to
const isInCheckout = (state) => {
return (
getIsShippingPage(state) ||
getIsBilingPage(state) ||
getIsConfirmationPage(state)
)
}
common pattern when writing mapStateToProps functions with selectors is to return an object with each key storing the result of a particular selector. The createStructuredSelector helper function in Reselect lets us write this pattern with the minimum of boilerplate. For example, if we writ
const mapStateToProps = createStructuredSelector({
title: getProductTitle,
price: getProductPrice,
image: getProductImage
})
it is equivalent to
const mapStateToProps = (state) => {
return {
title: getProductTitle(state),
price: getProductPrice(state),
image: getProductImage(state)
}
}
https://docs.mobify.com/progressive-web/0.15.0/guides/reselect/

Resources