React one time API call for multiple routes design - reactjs

I am new to React and trying to implement an application. Basically
my application have several routes. Each
route is backed by the same set of back end data +some api calls
specific to route taking back end data attributes as api params.
So I wrote a Higher order component to call the
API to retrieve the back end data,store the state in redux store and
pass it as props to the wrapped component which works fine.
const withData = (WrappedComponent) => {
class DataWrapper extends Component {
constructor() {
this.state = {
c: {},
d: []
};
}
componentDidMount(){
this.props.fetchData();
}
componentWillReceiveProps(nextProps){
this.setState({
c: nextProps.data.CSubset,
d: nextProps.data.DSubset
});
}
render() {
return <WrappedComponent {...this.props} {...this.state}/>;
}
const mapStateToProps=(state)=>({
data:state.appData
});
return connect(mapStateToProps,{fetchData})(DataWrapper);
}
export default withData;
class AppComponent extends Component{
componentDidMount(){
this.props.fetchComponentSpecificData(c.dataAttribute);
}
render(){
return <div />;
}
}
export default connect(null,{fetchComponentSpecificData}(withData(AppComponent);
But the issue is API gets called for all routes.It should be one
time per full application flow.
The component specific data fetch happens before the common back end data is available causing the component specific API call to fail.
User can type in the URL and launch
into any route within application and the API has to be called only
once and the HOC has to systematically route to the correct route
based on data.
Please advise on the design approach

I would say that the most "Reacty" way of doing this would be to implement the global API calls in the componentWillMount method of the top-level component (say, AppComponent). Right now, you have put it in a component that will be mounted for each subcomponent of your app, and it might be tricky to prevent the API call from being fired every time it mounts.
Using a HOC to provide those data to other components of your app is not a bad idea, but it makes your app a bit more implicit, and I don't think you're gaining a lot : you're adding a component with implicit behaviour instead of just adding a line in mapStateToProps.
Firing page-specific API calls after the global API call has succeeded is quite tricky. You're going to need a way to monitor the fact that the global API call has resolved, and in React/Redux way of thinking, this means dispatching an action. To dispatch actions asynchronously, you're going to need a Redux middleware such as redux-thunk or redux-saga (I believe redux-thunk is a lot easier for beginners). Now, if you have a way to know whether or not the global API call is a success, you can wait for a specific action to be dispatched (say, GLOBAL_API_CALL_SUCCESS). Now, this is the part where I'm doing myself a bit of publicity : I've written redux-waitfor-middleware that allows you to wait for a specific action to be dispatched. If you used it, it might look like this :
export async function fetchComponentSpecificData() {
await waitForMiddleware.waitFor([GLOBAL_API_CALL_SUCCESS]);
// perform actual API call for this specific component here
}
I'm not saying that waitFor middleware is the best tool to achieve this, but it's probably the easiest to understand if you're beginning!

Related

Update redux-store before ReactDOM.render(...)

Is there a way to update the Redux store before the ReactDOM.render(...) is runned?
Something like store.dispatch(...).then(() => ReactDOM.render(...))
Or a way to just replace the entire "state" of the store?
I am making an API with React components that is used inside another application with another framework, Dojo. Because of this we are using ReactDOM.render(...) and ReactDOM.unmountCompoentAtNode(...) when the component should appear/disappear.
So in the API, I am reusing the redux store, since it is not deleted from the memory/RAM even though a React component is unmounted via ReactDOM.unmountComponentAtNode(...). If I did not reuse the store, but made a new one every time the React component should render, the actions where "fetched by the Redux store" more and more times. Because of this I would like to ensure that I get to refresh the Redux store before I mount the component again.
Given that the rendered state should depend on the redux store, have you seen redux-thunk ?
One way is to have a show a spinner when the API endpoint is being reached out to.
After setting the state from the API response, go ahead and show the real component.
You are approaching the solution in the wrong way. For this use case, we have life cycle methods in components
ReactDOM.render(<App />, document.getElementById('root'))
In this code I am rendering App component, to solve your problem I'll use componentDidMount life cycle method of App and from there I'll dispatch the API call.
class App {
componentDidMount() {
dispatch(MY_ACTION)
}
render() {
return (<h1>dummy</h1>)
}
}
For hooks based functional component, you need to use useEfect
const App = () => {
useEffect(() => {
dispatch(MY_ACTION)
}, [])
return (<h1>dummy</h1>)
}

Where to Put Code that should run First ReactJs + Mobx State Tree

I have some code that grabs the users ipAddres. I do this right now in my componentDidMount in my app.js
async componentDidMount() {
await eventTrackingStore.getIpAddress();
}
So I did it in my app.js as it is my root component and I only want to set this once. This works fine if the user starts from the home page and navigates through the site.
However some pages can be loaded directly(ie you type in the url in your browser and it goes straight to that page).
Since the react lifecycle starts with most immediate component, which calls a method that expects the ipAddress code to be set but it does not get set till it hits the app.js
Now I could put the above code in each method but that gets tedious. Is there some sort of method in reactjs, or mbox or mbox state tree that would fire first?
If you use mobx-state-tree and you have a global store, then that global store can make the API call in the afterCreate method
const AppModel = types.model({
ips: types.array(types.string)
}).actions(self => ({
afterCreate() {
flow(function*() {
const ips = yield eventTrackingStore.getIpAddress();
self.setIps(ips)
})()
},
setIps(ips: string[]) {
self.ips = ips
}
}))
OR
The same thing you can do in a wrapped react component that wrappes every page of your app.
class App extends React.Component {
componentDidMount() {
eventTrackingStore.getIpAddress().then(res => {
// set the ips into a store or any logic you want in order to pass them down to children
})
}
render() {
return this.props.children
}
}
I can think of two solutions:
You can use react context
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Use context to share the store between all components and if the data is not loaded, initialize loading right there in that nested component.
If the data is already there then just take it,
getIpAddress method should return a promise, so in case when data is already there it will be immediately resolved.

React - accessing context in componentDidMount

I'm using Context API for the first time and I'm having some difficulties to understand how to update context in lifecycle methods. I had a look at the official doc and this answer which made sense but didn't help me much to find a solution.
I have 2 components for now: one parent and one child.
In the parent, I'm using an axios get() request in componentDidMount() to fetch data.
The child uses context to render elements so it needs the updated context once the request is complete.
My main issue is I can't find out where to update the provider state once the axios response is received. Ideally I'd like to assign the axios response to the provider state but I can't figure out how to access the context from there.
I made it "work" by assigning the axios response to the parent state then by updating the context provider state with it through a handler from my Provider and called this handler in render() under my Consumer since it's the only place I believe I have access to the context:
class ParentComponent extends Component {
render() {
return (
<MyContext.Consumer>
{(context) => {
if(axiosSuccess) {context.updateContextState(parentState);}
return (
<ChildComponent/>
)
}}
</MyContext.Consumer>
);
}
}
However I know it's not the right implementation since updating in render() is an anti-pattern.
What is the best way to update provider state in lifecycle methods?
Using context not differs much from usual state/props/redux usage. Pass handlers (beside values) from context the same way as from parent (by props). Move entire fetch to context (component) or at least pass handler to update state. Look at toggleTheme in docs.

converting react classes to functions with redux

I'm still new to react/redux, after getting something like this to function
User.js
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
return (
<Profile />
)
}
export default connect(null, {fetchUser})(User);
Profile.js
class Profile extends React.Component {
render() {
const { user } = this.props
return (
<h1>{user.profile.name}</h1>
)
}
const mapStateToProps = state => ({
user: state.store.user
});
export default connect(mapStateToProps, {})(Profile)
actions.js
export const fetchUser = (.....) => dispatch => {
fetch()
.....
}
reducers.js
case FETCH_USER:
return {
...state,
user: action.payload.user
};
As I understand it, the User component calls an action (fetchUser) from connect on componentWillMount(). That action calls an api, gets the data and the reducer adds that to the store within the state. The Profile component can then use connect to map the data from fetchUser in the store and display that data.
After reading some tutorials including https://github.com/reactjs/redux/blob/master/docs/basics/UsageWithReact.md
It looks like things can be simplified a bit without using classes.
If I were to change the User and Profile components to a more functional way, how would I do it?
eg.
const User = () => {
return (
<Profile />
)
}
how do I dispatch the fetchUser action and how do I simulate it to be called with the flow of componentWillMount()?
or am I just over complicating things?
There is also a way to support lifecycle methods in functional components.
https://www.npmjs.com/package/react-pure-lifecycle
import React from 'react';
import lifecycle from 'react-pure-lifecycle';
// create your lifecycle methods
const componentDidMount = (props) => {
console.log('I mounted! Here are my props: ', props);
};
// make them properties on a standard object
const methods = {
componentDidMount
};
const FunctionalComponent = ({children}) => {
return (
<div>
{children}
</div>
);
};
// decorate the component
export default lifecycle(methods)(FunctionalComponent);
I think you should keep using statefull components with redux...
https://medium.com/#antonkorzunov/2-things-about-purecomponent-you-probable-should-know-b04844a90d4
Redux connect — is a PureComponent.
Yes — a very important thing, a HoC for a molecule is a pure one. And works even inside other pure components. And gets store from a current context.
Same is working, for example, for styled-component — you can wrap it with PureComponent, but it will still react to Theme changes.
Solution is simple — bypass logic, use old school events bus, subcribe, wait and emit events.
Styled-componets:
componentWillMount() {
// subscribe to the event emitter. This
// is necessary due to pure components blocking
// context updates, this circumvents
// that by updating when an event is emitted.
const subscribe = this.context[CHANNEL];
this.unsubscribe = subscribe(nextTheme => { <----- MAGIC
React-redux:
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe =
this.store.subscribe(this.handleChange); <----- MAGIC
}
}
componentDidMount() {
this.trySubscribe();
}
Thus, even if parent Pure Component will block any update enables you to catch a change, store update, context variable change, or everything else.
So — something inside pure components is very soiled and absolutely impure. It is driven by side effects!
But this bypass straight logic flow, and works just differently from the rest of application.
So — just be careful. And don’t forget about magic.
Aaaand….
And this is a reason, why any redux store update will cause redraw in each connected component, and why you should use reselect just next to connect HoC —
to stop unnecessary change propagation.
But you should read this from another point of view:
redux-connect is a source of a change propagation.
redux connect is the end of a change propagation. It is still PureComponent.
And this leads to quite handy thing — you can control change propagation with redux-connect only. Just create a boundaries for a change. Lets talk about this in another article.
Conclusion
Pure components keep your application fast. Sometimes — more predictable, but often — less predictable, as long they change the way application works.
Stateless components are not pure, and may run slower than PureComponents by any kind.
But… if you very wish to create a fast application with good user experience — you have to use Pure Component.
No choice. But, now — you know hidden truth, and knew some magic…
React recommends that ajax request be made in componentDidMount(), rather than in componentWillMount(). For more info on this, read this post.
Since you want to make ajax requests in componentDidMount(), you need a class. There are two ways of writing component definitions: functional component and the class component. Functional components are more concise, but you don't get component lifecycle methods like componentDidMount(). Think of it as just a render function that takes props as inputs and outputs DOMs (in JSX). To override those lifecycle methods, you need to define them as a class.
If you want to use Redux, and want to make ajax requests in a Redux action, you should import the action creator function (fetchUser(..) in your case) that makes the ajax request, and dispatch(fetchUser(..)) in componentDidMount(). connect(..)ed components get dispatch(..) function passed to it by Redux store.
If you want to see how it's done in other redux apps, see the official example apps in the redux.js repo, paying attention to actions and containers: https://github.com/reactjs/redux/tree/master/examples
In Your case you can continue with statefull components no wrong in that
,If you need to go with functional way
There is a work arround
https://github.com/mobxjs/mobx/issues/162
Suggestion
Calling the api in componentDidMount will make sense than
componentWillMount , Because you can show the user something is
fetching.
I think,User component is designed nicely.It will act as a container for Profile to provide the Data.
Instead of making Profile component class oriented,it should be Stateless.
Lets User component pass the required data for Profile component.
You don't need to connect Profile component using redux-connect.Just render it as a Child component of User.
Profile
const Profile = (props) => {
const {user, likeProfile} = props;
//likeProfile()//call like this using dom event or programmatically.
return (
<h1>{user.profile.name}</h1>
)
}
You need to make some changes in User component.
Get the state for Profile component via mapStateToProps.
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
const {user, likeProfile} = this.props;
return (
<Profile user= {user} likeProfile={likeProfile} /> //passed the user data to Profile component vua User
)
}
Map the user state for Profile in User connect.
const mapStateToProps = (state)=>{
return{
user : state.somereducerkey.user //this will be accessible in Profile via props { user}
}
}
export default connect(mapStateToProps, {fetchUser, likeProfile})(User);

Creating React components that *may* be used in Redux application

Imagine you have a relatively simple component you create as part of a component library (simplified for brevity):
class ExampleComponent extends React.Component {
componentDidMount() {
getAsyncData().then((response) => {
const {a} = response.data;
this.setState({a});
this.props.notify({a});
});
}
render() {
return (
<h1>{this.state.a}</h1>
);
}
}
The component is required to allow dropping it into an application (think Google Maps for relatively similar approach) and have it just work. It can, however, share its data from response with the rest of the application, via some sort of callback (see this.props.notify above) it may receive via its props. This is an actual requirement and not a architectural choice.
Since this is a part of a library - you don't know what kind of application it is going to get used in at all times, but you do know that in many many cases it is going to get used in a Redux application.
For Redux application the above self-contained approach is not necessarily the best - as the retrieved data in response is better kept in application state in Redux store, where it can be consumed by other parts of application.
Even more so - the ExampleComponent itself is better off being "passive" and not having state at all, rather using mapStateToProps to have Redux infrastructure inject the state update into it via props.
The idea is that when ExampleComponent is in Redux application - its setState call and reference to this.state in its render method are somehow abstracted and "re-routed" to props via Redux?
One way would be to make ExampleComponent to use dispatch that by default calls setState and can be overridden by injected Redux dispatch - basically take this to Redux:
class ExampleComponent extends React.Component {
constructor() {
super();
this.dispatch = this.props.dispatch || this.dispatch;
}
componentDidMount() {
getAsyncData().then((response) => {
this.dispatch({type: 'SOME_ACTION', data: response.data});
});
}
dispatch(action) {
swtich (action.type) {
case 'SOME_ACTION':
const {a} = action.data;
this.setState({a});
case 'ANOTHER_ACTION': ...
}
}
render() {
return (
<h1>{this.state.a}</h1>
);
}
}
The above example works very well, save for:
this.state.a and its kin being sprinkled around the code whereas in Redux it should be this.props.state
having to do this.dispatch = this.props.dispatch || this.dispatch; in every component
I would like to avoid the obvious BaseComponent solutions that would abstract setState into some kind of hybrid... as this would take the code, with time, further away from "canonical" React.
Do you see an elegant way where the two approaches can be combined, with Redux superseding the inherent one?
You're making a fundamental mistake in thinking that a React component with Redux is different from a React component without Redux.
In fact, a React component is just a React component.
This is all your component needs to look like:
function ExampleComponent({ a }) {
return (
<h1>{a}</h1>
);
}
Simple, clean, readable, testable.
There's no obvious reason why your asynchronous data fetch should be buried inside the component's componentDidMount() method. It can be triggered anywhere else in the application. And it should be.

Resources