Ensure data "exists". React, or Endpoint? - javascript

I have a moreso best practices question regarding Flux/React for a SPA that gets most of it's data from REST endpoints.
In a React components Render function, when I do something like:
<MyComponent data={this.state.data} />
then in the component, I use {this.props.data}, whether for drawing a graph, text, whatever, it obviously causes problems if data is undefined or occasionally, 0.
Assuming this data lives in the store and is fetched aysnc (and thus also won't exist as soon as the app is loaded), where does it make the most sense to "check" or "ensure" data is defined and is a value that won't break child components? Should I init the data structure in the store will all null/empty values, or make my endpoints return the entire data structure in a similar fashion? Or, does it make sense to do data validation in the actual component and litter it with:
if (!(typeof(this.props.data) === "undefined")) {
Render Component as normal
} else {
Return null or an empty div or whatever
}
I feel like there should be a basic solution to this problem, but I feel like I'm constantly chasing down little things breaking on the off chance undefined/null/0 values "get through", for example drawing a D3 donut chart and every value in the array is 0.
Thanks!

Related

React Router v5 accompanied with Code Splitting, and Data Prefetching with the use of Server Side Rendering

I have a project where use react-router v3 only for one reason. The reason is the need of server side rendering with data prefetching and the most convenient way to do this is to keep centralized route config in an object or array and loop over the matching elements to fetch the data from the API on the server side. The data later is going to be passed to the client with the response HTML and stored in variable of JSON format string.
Also application uses code splitting, however with the use of babel-plugin-transform-ensure-ignore on sever side I can directly fetch the components instead of lazy loading and the native import method will be used only on client side.
Nevertheless, above-mentioned structure isn't working with react-router v5, as it's little bit difficult, since I can't use #loadable/components, as react-router official documentation suggests. Per my observation #loadable/components just generates the HTML on the server side instead of giving me the components in which I implement the fetch method responsible for server side logic.
Therefore, I would like to ask you the good structure for webpack + react-router v5 + ssr + data prefetch + redux + code splitting
I see it's quite complicated and no universal solution, however I may be wrong.
Any direction or suggestion is appreciated.
I have never tried #loadable/components, but I do similar stuff (SSR + code splitting + data pre-fetching) with a custom implementation of code splitting, and I believe you should change your data pre-fetching approach.
If I got you right, your problem is that you are trying to intervene into the normal React rendering process, deducing in advance what components will be used in your render, and thus which data should be pre-fetched. Such intervention / deduction is just not a part of React API, and although I saw different people use some undocumented internal React stuff to achieve it, it all fragile in long term run, and prone to issues like you have.
I believe, a much better bullet-proof approach is to perform SSR as a few normal rendering passes, collecting in each pass the list list of data to be pre-fetch, fetching them, and then repeating the render from the very beginning with updated state. I am struggling to come up with a clear explanation, but let me try with such example.
Say, a component <A> somewhere in your app tree depends on async-fetched data, which are supposed to be stored at some.path of your Redux store. Consider this:
Say you start with empty Redux store, and you also have you SSR context (for that you may reuse StaticRouter's context, or create a separate one with React's Context API).
You do the very basic SSR of entire app with ReactDOMServer.renderToString(..).
When the renderer arrives to render the component <A> somewhere in your app's tree, no mater whether it is code-splitted, or not, if everything is set up correctly, that component will have access both to Redux store, and to the SSR context. So, if <A> sees the current rendering happens at the server, and there is no data pre-fetched to some.path of Redux store, <A> will save into SSR context "a request to load those data", and renders some placeholder (or whatever makes sense to render without having those data pre-fetched). By the "request to load those data" I mean, the <A> can actually fire an async function which will fetch the data, and push corresponding data promise to a dedicated array in context.
Once ReactDOMServer.renderToString(..) completes you'll have: a current version of rendered HTML markup, and an array of data fetching promises collected in SSR context object. Here you do one of the following:
If there was no promises collected into SSR context, then your rendered HTML markup is final, and you can send it to the client, along with the Redux store content;
If there are pending promises, but SSR already takes too long (counting from (1)) you still can send the current HTML and current Redux store content, and just rely on the client side to fetch any missing data, and finish the render (thus compromising between server latency, and SSR completeness).
If you can wait, you wait for all pending promises; add all fetched data to the correct locations of your Redux store; reset SSR context; and then go back to (2), repeating the render from the begining, but with updated Redux store content.
You should see, if implemented correctly, it will work great with any number of different components relying on async data, no matter whether they are nested, and how exactly you implemented code-splitting, routing, etc. There is some overhead of repeated render passes, but I believe it is acceptable.
A small code example, based on pieces of code I use:
SSR loop (original code):
const ssrContext = {
// That's the initial content of "Global State". I use a custom library
// to manage it with Context API; but similar stuff can be done with Redux.
state: {},
};
let markup;
const ssrStart = Date.now();
for (let round = 0; round < options.maxSsrRounds; ++round) {
// These resets are not in my original code, as they are done in my global
// state management library.
ssrContext.dirty = false;
ssrContext.pending = [];
markup = ReactDOM.renderToString((
// With Redux, you'll have Redux store provider here.
<GlobalStateProvider
initialState={ssrContext.state}
ssrContext={ssrContext}
>
<StaticRouter
context={ssrContext}
location={req.url}
>
<App />
</StaticRouter>
</GlobalStateProvider>
));
if (!ssrContext.dirty) break;
const timeout = options.ssrTimeout + ssrStart - Date.now();
const ok = timeout > 0 && await Promise.race([
Promise.allSettled(ssrContext.pending),
time.timer(timeout).then(() => false),
]);
if (!ok) break;
// Here you should take data resolved by "ssrContext.pending" promises,
// and place it into the correct paths of "ssrContext.state", before going
// to the next SSR iteration. In my case, my global state management library
// takes care of it, so I don't have to do it explicitly here.
}
// Here "ssrContext.state" should contain the Redux store content to send to
// the client side, and "markup" is the corresponding rendered HTML.
And the logic inside a component, which relies on async data, will be somewhat like this:
function Component() {
// Try to get necessary async from Redux store.
const data = useSelector(..);
// react-router does not provide a hook for accessing the context,
// and in my case I am getting it via my <GlobalStateProvider>, but
// one way or another it should not be a problem to get it.
const ssrContext = useSsrContext();
// No necessary data in Redux store.
if (!data) {
// We are at server.
if (ssrContext) {
ssrContext.dirty = true;
ssrContext.pending.push(
// A promise which resolves to the data we need here.
);
// We are at client-side.
} else {
// Dispatch an action to load data into Redux store,
// as appropriate for your setup.
}
}
return data ? (
// Return the complete component render, which requires "data"
// for rendering.
) : (
// Return an appropriate placeholder (e.g. a "loading" indicator).
);
}

plotting real time data of mobile's accelerometer against time axis in react native

I'm plotting real time data of accelerometer of mobile against time axis.x-axis will have time and y-axis will show values of accelerometer. I'm using react-native-highcharts for plotting data. But the output graph is blank. The code for live data plot is given at the URL https://github.com/TradingPal/react-native-highcharts . In this code I'm just replacing y with x-values of accelerometer.
output is:
You are mutating state, which is a big no-no in React.
State should be immutable at all times, and only changed via a call to setState. If you are just directly mutating it like in your example, your component will not update.
I would advise you to learn more about component state in React (same in React Native) then you will realize how you can refactor your calls to this.state.dataArr.push and such.
render should be a pure function of props and state.
As we don't see the entire picture, only the render method (in the future please try to post a complete example) we can only make assumptions here.
I would imagine that in some of your other code you are recording the accelerometer and call setState to change accelerometerData. (If you are directly changing it via a regular assignment, change it to a setState call.) Then instead of mutating dataArr in the render method, change dataArr using the same setState call.
p.s. This is still not a perfect solution as it would still be using derived state, but that is another - and slightly more advanced - topic to talk about.
Your code as it is, always uses the latest Accelerometer data, i.e. you're always plotting one point (latest), and so the graph seems empty.
What you should do, make accelerometedData an Array in your state definition and then in subscribe push the data to it, instead of setting the latest point using setState, and plot from it to plot multiple points.
EDIT
To clarify:
Your state should be something like
state = {
accelerometerData: []
};
Then your subscribe would be:
_subscribe = () => {
this._subscription = Accelerometer.addListener(accelerometerData => {
let data = this.state.accelerometerData;
data.push(accelerometerData);
this.setState({
accelerometerData: data
});
});
};
And then inside render() you have this.state.accelerometerData which is arrray that you can use via this.state.accelerometerData.map() or similar

Does passing data in props duplicate that data?

I'm building a dashboard in React, and currently I'm looking at building a small button component that will allow a user to download data from a chart as a CSV file. Something like this:
// Dashboard
render(){
const data=makeLargeDataset(); // make an array with 1m rows
return (
<DownloadCSV data={data} />
);
}
// Download Button
onClick(){
if (this.props.data){
convertAndDownload(this.props.data);
}
}
Does this duplicate the 1M rows when passing to the button's prop? If I had to pass a prop through several layers, will it keep duplicating? Is there a smarter way to pass props -- or work with large variables and arrays, in general -- so it isn't as wasteful or inefficient?
Nope, It does not.
Any props passed from a higher order component to a child component always references the same props. It's called Single Source of Truth. You can alter one data point in the HO Component and React as the name tells reacts to only the change and nothing else.
A simple way for holding such big data sets would be to use a store like Redux to keep the data safe but the smarter way would be to create an API or a micro service based on your needs and call that rather than loading a million rows on the browser.
You are not passing the data copy, you are passing the reference.
You could think about paginating your data in some way in order to handle lower sized arrays.

Resetting redux state without breaking react components

Been working with redux in a complex react application.
I put some data in redux state and use this data to render a component.
Then, you want to change the page and I do need to reset some fields in the redux state. Those fields were used to render the previous component.
So, if I reset the state before going to the next page, the previous page rerenders and throws errors because data is missing (because of the reset). But I can't reset in the next page, because that page is reached in many flows so it can be difficult to manage when to reset and when not.
How problems like this are managed in react applications?
All the example are simplified to show the problem. in the actual application, there are many fields to reset.
so, page 1
redux state
{field: someValue}
component uses the field value
function myComponent(props) => {
return (props.field.someMappingOperation());
}
Now, when going to page 2, field should be null, so we reset it
redux state
{field: null}
the component above rerenders, throwing an error because of
props.field.someMappingOperation()
and field is null.
What can be done is to load the next page, so this component is not in the page and then reset the state. yet, this becomes very hard to manage, because you the are in page B (suppose clients list with the list saved in redux) and you want to see the details of a client you go to page C, when you press back you don't want to reset again the state. So you add conditions on the reset. But because there are many flows in the application, that page can be reached in many ways. Have conditions for each way is not the best solution I suppose.
Edit:
I would like to add that state reset was not required initially and components aren't designed for that. As the application grew and became enough complex it became necessary. I'm looking for a solution that does not require me to add props value checking in every and each component, like
{this.props.field && <ComponentThatUsesField />}
This is really a lot of work and it's bug-prone as I may miss some fields.
As the application has a lot of pages and manages a lot of different data, the store results to be big enough to not want to have clones of it.
You have to design your components in a more resilient way so they don't crash if their inputs are empty.
For example:
render() {
return (
{ this.props.field && <ComponentUsingField field={this.props.field} />}
)
}
That's a dummy example, but you get the point.
you can change your code like below:
function myComponent(props) => {
const {fields} = props;
return (fields && fields.someMappingOperation());
}
This will not throw any error and is a safe check
You don't need to use conditions or reset your state, your reducer can update it and serve it to your component. You can call an action in componentDidMount() and override the previous values so they will be available in your component. I assume this should be implied for each of your component since you are using a different field values for each.

Preventing react-redux from re-rendering whole page when state changes

I am reading several articles about how to prevent react-redux from re-rendering the whole page, when only one little thing changes.
One article suggests that instead of wrapping all into one big container (as in figure 1 here) wrapping all into smaller containers (as in figure 2 here). If something changes in Container 2, only Component 2 and Component 3 are getting re-rendered. Component 1 would not re-render.
Figure1
Figure2
I have following questions:
If I wrap everything in smaller containers, I would need "several" global states, for each container one (as indicated with the pseudo-code on the bottom of the figure). Is that common practice?
If it is ok to have "several" global states and I would need in some property from Container1 in Container2, I would need to connect that with two global states. To me that feels like it could get messy very quick. Where does what come from?
When and where would I use the react method shouldComponentUpdate()? Using the Big Container approach how would I differ which Component should be rerendered?! If implemented in the Components, they would not be "dump" anymore, because they need to access the global state in order to decide whether to re-render or not. I would not be able to reuse Components because every Component has its own special case when to rerender and when not. I am not sure where and when to use shouldComponentUpdate()
Please note that I am pretty new to this and might have made wrong assumptions etc. I basically want to know how not to re-render the whole page, when only one thing needs to be updated. The results from asking google differ a lot.
Your second approach is the way to go, though your definition of a global state is a bit misleading.
Basically, you want to have exactly one "global state". This is what is referred to as "store". All components that need to receive parts of the store are connected to it using react-redux' connect function.
Now, connect(...) is actually a HOC which wraps your component and passes only defined parts of the store to it. This way, the component (and its' children) only re-render when its' defined props change.
Don't be afraid to use connect() more often. You just have to be careful what parts of the store you pass to the container and this is exactly where performance can become an issue.
This should answer your first question. The second one is a question of design. Design in terms of how your app and maybe also in terms of how your datasource is structured. As said before, you want to have a minimum of props passed to a component so it doesn't re-render when other parts of the store change.
For the third question, you first have to understand that 'dumb components' can, of course, receive props from their parent components/containers. Dumb just means that they don't get to decide whether a re-render should happen or not. Dumb components are there to present/display data and that's it.
Let's say you have a really simple store:
const store = {
posts: {
all: [],
isFetching: false,
err: {},
}
}
And you connect your container to it like this:
function mapStateToProps(store) {
return {
posts: store.posts.all,
isFetching: store.posts.isFetching,
err: store.posts.err,
};
}
#connect(mapStateToProps)
And this container has three dumb components it can use:
A posts component, which receives all posts and displays them using another dumb child (pseudoCode, you get the point):
function posts = (posts) => {
posts.map((post, id) => (
<otherDumbComponent post={post} key={id} />
));
}
One to display just a spinner while isFetching
One to display the error if there's one.
Now, if only isFetching has changed, only the second component will re-render and that's it. Oh, and shouldComponentUpdate() is something you probably don't want to use, because, well.. there are many good blog posts about it.

Categories

Resources