Redux container inside presentation component with API calls - javascript

Redux allows using complex container components inside other "dumb"/presentation components. Point is that mostly the simple components are const literal functions, so with stateless mod it will re-render...a lot.
My concern is that it will also call the components it contains on each render, including these containers that can trigger API calling actions eg. inside componentDidMount or other life cycle method.
My current solution is a 'cache' inside store, I trigger api method, but inside I check, should the request be made*/is cache invalidated to continue or break the API call.
Problem is that these components in my opinion should not be tight coupled, so I cannot trigger the load after some other actions inside totally different component, cause that way it won't be independent.
* - due the fact that store in this case is just a simple map, I check whether the data already exists(or timestamp is outdated) or is it just missing.
Just to picture it better:
<SimpleView>
<Container/>
</SimpleView>
(...)
class Container extends React.Component {
(...)
componentDidMount() {
apiCallingActionTrigger()
}

Related

Knowing when the UI is ready in React / Redux

I would like to know how long it takes my app to become "ready" for the user to interact with it. The timeline of the app loading includes the following:
DOM load.
Initial React render.
HTTP calls triggered from various componentDidMount()s
HTTP calls return. Redux state is updated with HTTP responses.
React renders with new values from HTTP responses.
All initial loading is complete. The user is ready to interact with the app.
At the end of this process, I'd like to fire a tracking event to record the value of window.performance.now().
I'm not sure what the best way to do this is. React's component system does a good job of decoupling various parts of the UI. In this case, it, unfortunately, means that I'm not going to have one easy place to check to know "has everything been rendered with the new data".
Things I've tried or considered:
Look for a React lifecycle hook that tells me if any child is updating. Something like componentOrAnyChildDidUpdate(). I don't think this exists, and it may be counter to the React philosophy.
Hack together an anyChildDidUpdate() lifecycle hook via context and making every component in my app either subclass a helper abstract base class or be wrapped in a higher-order component. This seems bad because context is an experimental API.
Subscribe to the Redux state via store.subscribe(). Update the Redux state to have an explicit record of whether all the HTTP calls have returned. Once all the HTTP calls have returned, and React is finished re-rendering, then I know to fire the tracking callback. The problem is knowing when React is finished re-rendering. It would work if my store.subscribe() the callback was guaranteed to be executed synchronously after react-redux's callback. But that is not the case.
Is there a good way to do this in React?
I'm afraid there is not a universally good way to do this in React, this is "logic" that is related with the structure of your application.
I wanted to display a loader when navigating from one page to another in Single Page Application(SPA) written in react and I faced a similar problem not knowing when to stop displaying it and if all the components in the new page have completed their API calls/renders.
The way I resolved it was:
1) I removed all my API calls from inside my components.
2) Create a Page component to wrap all the components included in that page(invoked by your react router).
3) Perform all requests required for your components simultaneously on navigation(in my case while displaying the loader). For each request that completes, create a promise and inside it create your component dynamically using React.createElement. Pass to the component created the request response and the promise handler as props.
4) Resolve the promise in your component's componentDidMount.
5) Once all the promises are resolved you know the "page" is ready and you can record the value of window.performance.now().
It is hard to provide a minimal code example without a lot of out of context code since this code is spread across the application.
There's more than one way to do what you're asking - but off the top of my head I would do the following:
• Create a perf reducer and call performance.now() right before I make the redux store and add the value to the initial state.
{ perf: { start: '...', end: '...' } }
• Track loading status of initial HTTP requests in a loaded reducer.
{ loaded: { request1: false, request2: false, request3: false } }
• Connect top level component to loaded reducer and check if all requests are complete in componentDidUpdate. If true add end value to perf reducer.
import React from 'react';
import { connect } from 'react-redux';
class App extends React.Component {
componentDidUpdate(prevProps) {
if (!prevProps.loadingComplete && this.props.loadingComplete) {
this.props.updatePerf(performance.now());
}
}
}
const mapStateToProps = state => ({
loadingComplete: state.loaded.every(item => item),
});
const mapDispatchToProps = dispatch => ({
updatePerf(time) {
dispatch({ type: 'SET_ENDING_PERF', payload: time });
},
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
In our project, we use redux and a couple of actions - trigger, request, success, error.
"Trigger" calls request and throw loading: true into our "smart" component
"Request" fetch data and throw them into Success/Error
"Success", tells us that everything is fine and data loaded and throw { loading: false, data }
On success/error, we always know that is happening with our application
And of course you can use componentWillReceiveProps(nextProps) {} and check loading flag and data in your component
You can try to use react-addons-perf
That's how I benchmark my apps usually:
Basically you want to start measuring when your component starts updating and stop the measurement after the lifecycle method componentDidUpdate().
import Perf from 'react-addons-perf'
componentWillMount() {
window.performance.mark('My app');
}
componentDidMount() {
console.log(window.performance.now('My app'));
Perf.start()
// ... do your HTTP requests
}
componentDidUpdate() {
Perf.stop();
Perf.printInclusive();
}
Like that you will track your component initial render + your component updates.
Hope it helps
Two steps to achieve this.
Track all HTTP-requests via actions
Simple counter in redux state would be enough, that increments on start of request and decrements on success or fail (all via appropriate actions).
No window.dirty.stuff here in front of Dan Abramov.
Track all connected components
componentDidUpdate method indicates that all children finished rendering. This can be achieved without much boilerplate with recompose's lifecycle HOC. Right place to start tracking re-render might be sCU method or just cWRP if you use recomposes shouldUpdate or reselect already - since we won't track components that didn't update.
Note that all is assuming idiomatic react/redux app, no tricky side-effects or animations. For example, if some child component somewhere deep in the hierarchy fires its own AJAX request, say on cDM, not via redux action. This way you have to track them too, BUT no one can be sure if they imply any re-renders.
Tracking such behaviors is still possible, just might require more effort.
from a design POV, in my opinion being ready or not is a user presentation property and so should be handled by react components alone.
for example, you may later decide to allow the user to interact with some part of your UI while some other parts are still loading, or you may decide that some components should allow the user to interact before the data model is ready, etc ... in other words, it's a (react) component responsability to tell if/when it's ready or not and nobody else.
That is, all 'waitable' components could expose an onLoad-like prop to be passed up the react tree; the root component would then coordinate the result by changing the tree down stream accordingly by propagating the appropriate props down to leaves.
This could be implemented by writing a mixin/high order component to incapsulate the "readyness update" logic.

How to call an API and update one child from another child in react/redux

I am using redux, react, react-router and reselect. There is a screen that contains two components, the Header and the Body. There is a component in the Header to select an ID. When a new ID is selected a new action is dispatched to update the ID in the state in redux.
Based on that ID there should be an asynchronous API call to load the element from the server and store it in the state, which should then trigger a render of the screen and display the element in the Body. The loaded element would then be extracted from the state with reselect.
The real-life scenario is a bit more complex than that, because there are many different Body components with different API calls and only one Header component. But they can be treated as different screens. So, for example:
Screen 1:
components: Header, BodyTypeA; API to call: apiA
Screen 2:
components: Header, BodyTypeB; API to call: apiB
My understanding is that the API call should be done in the reducer when it stores the new ID in the state. But how to determine which screen is being shown and which API to call if the action is dispatched by a component shared across screens? Can I somehow subscribe in the Body to changes to the ID and trigger the API call from within the screen? The screen type is determined by the react-router. Would the reducer have any access to the router to determine which screen is being shown? Are there any best practices for handling such a scenario?
Seems like you may be overthinking this. So, one thing at a time:
My understanding is that the API call should be done in the reducer when it stores the new ID in the state.
Unless you'll be using redux-loop that allows returning an action from a reducer you're wrong. In principal reducers CAN NOT dispatch any actions, they only consume them. It's the middleware layer (or action creator) where you dispatch your actions and may call your APIs.
But how to determine which screen is being shown and which API to call if the action is dispatched by a component shared across screens?
To determine "where you are" it's best to keep routing information in your state tree. Since you're using react router anyway consider using https://github.com/reactjs/react-router-redux to expose route information in your state tree and then access it from your middleware / action creators / selectors.
Can I somehow subscribe in the Body to changes to the ID and trigger the API call from within the screen?
Yes, technically you can subscribe to store changes, but normally you wouldn't have too - see my other points and hopefully it's clear enough.
The screen type is determined by the react-router. Would the reducer have any access to the router to determine which screen is being shown?
No (in general) reducers only have access to their slice of state, so while your reducer may react to react router actions (if you'll use react-router-redux - see above) to store route details you require, but in principle it should be up to your selectors and/or connected components to construct / pull data required for given component from the state tree.
Are there any best practices for handling such a scenario?
I guess use react-router-redux, introduce a middleware that will trigger an API call in response to your action and will dispatch consecutive action containing data returned from the API. That action you'll handle in your reducer, storing data in the tree.
And a random tip, if you have components like bodyTypeA, bodyTypeB then those components can (should even) keep information if they're A or B and propagate that to the action creator. E.g. if you have an action creator called requestForId(ID) you'd modify signature to be requestForId(ID, type) and in your component / callback (whenever you're invoking that action creator) you'd be passing not only the ID but also the type, i.e. bodyTypeA would call it with requestForId(ID, 'A').

How should child components in React communicate between each other in a clean and maintainable fashion?

I'm fairly new to React and I'm trying to understand a clean way for child components to communicate with each other.
In a simple component, I know that I can make use of props to pass data to child and callbacks for children to pass data back to parent component.
In a slightly more complex case, when I have multiple children components in a parent component, the communication between the children gets a little confusing. I'm not sure what I should do for children components of the same level to communicate with each other.
In my case, I decided that, maybe, I could use states. So I will have a state value in the parent component, and pass it on the children's props. Similarly, the callback handlers (called from the children component) in the parent component will help to set the states accordingly so that a state value gets passed on from one child to another through React's binding.
And a pseudo code could look something like:
//Inside Parent Component
constructor() {
//initialise state for the child components
this.setState({testList: []});
}
render() {
return (
<div>
<ChildA onSomething={this.onSomethingHandler} testList={this.state.testList} />
<ChildB onSomethingElse={this.onSomethingElseHandler} testList={this.state.testList} />
</div>
);
}
onSomethingHandler(evt):void {
if(blah.blah.blah) this.setState({testList: this.state.testList.splice().push(evt.value)};
}
onSomethingElseHandler(evt):void {
//Some other complex biz logic...
if(blah.blah.blah) this.setState({testList: this.state.testList.splice().push(somethingOtherStuffDueToLogic)};
}
//Inside ChildA Component
export IChildAProps {
onSomething: (evt)=>void
}
render() {
//Do some logic from the value in testList property
if(this.state.testList == blah blah)...
return (
<button onClick={this.props.onSomething({id:321, value:"wassup! I'm ChildA."})}>ChildA</button>
)
}
//Inside ChildB Component
export IChildBProps {
onSomethingElse: (evt)=>void
}
render() {
//Do some logic from the value in testList property
if(this.state.testList == blah blah)...
return (
<button onClick={this.props.onSomething({id:123, value:"yo! I'm ChildB."})}>ChildB</button>
)
}
At this point, I'm starting to wonder if the logic in that 2 handler methods, namely onSomethingHandler() and onSomethingElseHandler() in the Parent component, should actually be resided inside the child components themselves? I thought of this because those logic look like something the child component should be handling on their own to serve their purpose. The parent component shouldn't do it for them or it might just grow messy. But I've no choice because of how I'm handling their communication. Apart from this, I also created a new state simply just to allow them to communicate.
So far, this is still relatively manageable. But in my own experiment, it has got to a stage where I've children component nested inside another children components that need to communicate across other children components of the same (or sometimes different) level. Using states for communication also meant that I have many states all over the place, which doesn't look like a good idea to me. And the parent components ended up with tons of messy callback handler methods to manage all that propagation of data up and down the component tree.
The situation is so messy that I can at most illustrate it as something like so:
And you can see in the above illustration, ChildB ended up having yet another state just to help passing that information between its children components.
I'm sure I'm missing something that I should know about React. The callbacks I'm having in the parent components seem a little too much just to handle data propagation. How should I really organise the communication of children components in a clean and maintainable way?
Every React programmer hits this wall at some point in time. I did too. The answer is to use Redux for state management. You have experienced how tedious it is to use React's native state.
Redux is a state management mechanism which can be used in conjunction with React. So you won't be using React's state, instead you will use Redux.
Redux provides a single store, where the state of entire application is stored. You can access the state in your components using connect() method.
But there is a caveat. Not all of the react components are connected to the Redux store. There are two types of components-
Smart/connected components: Connected to redux store
Dumb components: Dependent on connected components
The idea is to pass the state from redux store to Connected components via React's props. The connected components can directly consume state from the store. The dumb components are not directly connected to the redux store. The connected components also pass the state to the dumb components via props. So you see, React's state is bypassed altogether. Now, if you want to change the state, following events must happen-
An event is fired from the smart/dumb component.
Actions are dispatched to the store
Reducers create a new state according to the actions.
A new state will be stored in the store.
Store will return new state to the connected components via connect() through props
Dumb components will receive new state from connected components through props
What are actions and reducers?
Actions are nothing but javascript objects that describe how to change the state.
Reducer is a "pure" function which builds and returns the new state tree according to the action dispatched to the store.
Redux - http://redux.js.org/
Redux-thunk - https://github.com/gaearon/redux-thunk
Redux-saga - https://github.com/yelouafi/redux-saga
Most fashion way is using Redux.js (or flux.js) to matain your child components state.
http://redux.js.org/
If you don't like invoke third party js. You can use refs property:
https://facebook.github.io/react/docs/more-about-refs.html
We can use the react context API,
Context provides a way to pass data through the component tree without having to pass props down manually at every level
Also, note that Mark Erikson has mentioned in his blog,
Yes, the new context API is going to be great for passing down data to deeply nested components - that's exactly what it was designed for.
If you're only using Redux to avoid passing down props, context could replace Redux - but then you probably didn't need Redux in the first place.
Context also doesn't give you anything like the Redux DevTools, the ability to trace your state updates, middleware to add centralized application logic, and other powerful capabilities that Redux enables.
To handle scenarios you mentioned, context API is a good option and you don't have to use additional libraries for that.

Communication between Reactjs Components

After struggling too much with Redux, flux and other pub/sub methods i ended up with the following technique. I do not know if that can cause some big damage or flaws so posting it here to get some light from the experienced programmers about its pros and cons.
var thisManager = function(){
var _Manager = [];
return{
getThis : function(key){
return _Manager[key];
},
setThis : function(obj){
_Manager[obj.key] = obj.value;
}
}
};
var _thisManager = new thisManager();
// React Component
class Header extends Component{
constructor(){
super();
_thisManager.setThis({ key: "Header", value:this}
}
someFunction(data){
// call this.setState here with new data.
}
render(){
return <div />
}
}
// Then from any other component living far somewhere you can pass the data to the render function and it works out of the box.
i.e.
class Footer extends Component{
_click(e){
let Header = _thisManager.getThis('Header');
Header.somefunction(" Wow some new data from footer event ");
}
render(){
return(
<div>
<button onClick={this._click.bind(this)}> send data to header and call its render </button>
</div>
);
}
}
I am sending json as a data in my application and it perfectly renders the desired components and i can invoke the render without any pub/sub or deep passing down the props to invoke a parent method with a changing this.setState to cause re-render.
So far the application works fine and i am also loving its simplicity too. Kindly throw light on this technique pros and cons
Regards
EDIT:
It is bad to call render so i changed it to another method to get more pros and cons of this setup.
Two main concerns with this setup:
1. You should never call react lifecycle methods directly
2. Backdoors into components are a bad idea, which destroy react's maintainability
Ad 1:
If you invoke render() (or any other react method) directly, react probably does not call componentDidMount(), componentDidUpdate()` and other lifecycle methods in the component tree.
Dangers are:
Many designs with react component rely heavily on the lifecycle methods being fired: getInitialState(), componentWillReceiveProps(), shouldComponentUpdate(), componentDidMount(), etc etc. If you call render() directly, many components will likely break or show strange behaviour.
You run the risk of breaking react's difference engine: through life-cycle management, react keeps a virtual copy of DOM in its (internal) memory. To work correctly, the integrity of this copy if vital to react's working.
Better would be (but still in violation of my second point):
Include a different method inside the component.
Which has a setState() if you want to re-render.
And call that method from the outside.
Ad 2.
A direct reference to a mounted component (as your thisManager does) has some additional risks. React's designs and limitations are there for a reason: to maintain unidirectional flow and component hierarchy with props and state, to make things easy to maintain.
If you break this pattern - by building a backdoor into a component, which allows manipulation of state - you break this design principle of react. It is a quick shortcut, but is sure to cause great pain and frustation when your app grows.
As far as I know, the only acceptable exceptions to this rule are:
Methods inside a component that respond to ajax call results, to update state (e.g. after fetching data from server)
Methods inside a component to handle triggers from its direct descendent children components (e.g. run validation on form after a child button has been clicked)
So if you want to use it for that purpose, then you should be fine.
Word of warning: the standard react approach guards random access to components, because the invoking component or method needs to have a reference to the component. In both examples such a reference is available.
In your setup, ANY outside piece of code could lookup the ref to the "header" in your table, and call the method which updates state.
With such indirect reference, and no way of telling which source actually called your component, your code is likely to become much harder to debug/ maintain.

Caching props in getDefaultProps an anti-pattern in React?

I am writing a complex react app and using Cortex as my central model. The philosophy with cortex is that it wraps your data and on changing the data, calls a complete re-render from the root. This works great especially when you have non hierarchical views changing state and affecting the other.
The issue that I am facing is maintaining states/props on re-render. For example I have a certain hierarchy which goes like this:
<Page>
<EditorCard>
<Editor/>
<PublishButton/>
</EditorCard>
</Page>
The EditorCard needs the JavaScript instance of the Editor in order to make changes to the Editor on clicking the PublishButton (I am using an external library inside Editor which exposes methods for editing). Hence the Editor on ComponentDidMount sets the instance as a prop on the EditorCard by calling a function passed down to it.
My issue is that when I click the PublishButton I change the value of the cortex object which causes a re-render from the root and I loose the props for that Editor (since component is already mounted ComponentDidMount is not called again).
The way I took care of this problem is by caching of getDefaultProps.
Inside EditorCard my default props are:
getDefaultProps: function() {
return {
cachedData: {},
}
},
And is saving the editor instance as this.props.cachedData.editor = editorInstance
This saves props over multiple re-renders.
Is this how getDefaultProps caching was meant to be used? On saving props over multiple re-renders am I breaking some of the core react rules with this hack? Could you suggest a better structure if so?
No, getDefaultProps is what it means to be: getting the default props in case the owner hasn't passed those to you. You could say it's a shorthand for a = this.props.bla || 'hello';.
That being said, if I'm understand your question correctly, I see three ways to solve it.
Cache that in your state instead. Props are passed by the parent and are meant to be read from, inside the child, at least in vanilla React.
Instead of putting that props passing logic in your componentDidMount, why not put it in componentDidUpdate?
ref lets you grab the instance and call its methods directly.

Categories