Reactjs render component only when ALL its child done thier render - javascript

I have a "slow" loading issue in one of my pages.
That page contains a "root" component - let's call it Container.
My Container makes 2 fetch calls upon a value selection from a dropdown list.
The fetched data (A and B) are passed to a child component (Board).
Board is built from 2 unrelated components - Player and Item and they use A and B respectively as initial data.
It so happens that the Players render process is much more heavy than Item's.
The result is that Item is rendered on the page and after some few seconds Player is rendered also.
My wish is to see both rendering at the same time - how can I achieve that?
I guess it is something should be done by Board but how?
It is also important that this "waiting" will take place every time the value from the droplist is changed (it always there for the users to choose).
Thanks!

There are a couple of ways to do this.
1) Use graphql to orchestrate the loading of data, so that the page renders when all of the data is available
2) Set state values for playerDataReady and itemDataReady. If either of these is false, display a loading spinner. When they are both true (as a result of ajax calls being resolved), then the render will do both at the same time
In the constructor...
state = {
playerDataReady: false,
itemDataReady: false,
}
:
:
componentDidMount() {
// Request player data, using axios or fetch
axios.get(...)
.then(data) {
this.data = data
this.setState({playerDataReady:true})
}
// Do the same for your item data (and update itemDataReady)
}
In your render function:
render() {
if (!playerDataReady || !itemDataReady) {
return <Loader/>
// The data has arrived, so render it as normal...
return ....
You can fill in the details

Related

React hooks context component doesn't re-render when context state changes

I am working on a react app that should allow a user to browsers records from a database one page at a time.
I am using functional components and react context to manage state. I have a useEffect that will pulls a function from my Context to fetch records from the database and puts them into the Context State. The component pulls the array of records from the Context State and if they is at least 1 (array length > 0) it will map the array using each record to populate a record list item component.
The data is pulled and the React Dev Tools shows that the data is in the Context. I can console log the records array (with records) to the console and I can even log the list of elements that are the Record List Item Components to the console, but the page always show the "No Records Found" message that you should get if the array is empty (length of 0).
The main component is:
<ul className="list-group">
{
records.length > 0
? (records.map((rec) => <RecordListItem record={rec}/>))
: (<li className="list-group-item">No Records Found</li>)
}
</ul>
the useEffect in the RecordsList is:
const {
filter,
getRecords,
page,
perPage,
records,
} = useContext(RecordsContext);
useEffect(() => {
const start = (page - 1) * perPage;
getRecords(start, perPage, filter); // filter, page, and perPage come from the Context
}, []); // only want this useEffect to run once when the component loads.
getRecords is a function that queries the database for records starting from one record and going up to the number of records per page.
const getRecords = async (start, limit, filter) => {
if (filter) {
const items = await db("contacts").select()
.where({status: filter})
.offset(start).limit(limit);
} else {
const items = await db("contacts").select()
.offset(start).limit(limit);
}
dispatch({type: SET_RECORDS, payload: items);
The records are showing up in the dev tools and in the console logs. I am not seeing a problem with the database connection or queries.
The only problem I am seeing is that the component doesn't update when the state inside the context changes. This is only happening with this one element. Other state values are changing and the components that use them are updating.
For example. When the filter is picked the number of records updates to reflect the total number of records with that filter. Adding, updating, and deleting records in the database also cause these numbers to update when the components are refreshed.
I have another useEffect that will trigger when the page or perPage changes and another that resets the page to 1 and getsRecords again when the filter changes. Both of these useEffects are currently not in the code to avoid them causing issues.
The Parent Component is the RecordsViewer. This is simply just a shell to hold the other elements in. It creates a Navbar at the top that gives filter options as well as paging and items per page options and a section with a container
The RecordsList is a card that contains the List-Group element and gets the data from the Context. It then maps over the fetched records and passes each record to a display component.
The RecordsContextProvider wraps the RecordsViewer and I can see the data in the Context within the React Dev Tools. Also doing a console.log(records) does show an array of the correct number of items. I have tried capturing the map of records in a value and then showing that, but no change. I can console.log that variable and see it is an array of react elements of type "LI".
I am completely lost as to what the hang up is here.
I've found the issue. Misspelled length as lenght in the condition to check if the array was empty or not.
What I get for trying to work after less than 5 hours of sleep.

Unable to post data from child to parent in react.js

I'm working in single page web application. In page i have a dropdown so based on dropdown value i will load the data in grid. The grid data is come from jqgrid component so sometime grid data is still loading and user change the dropdown so previous server request is not complete and user hit a new request to server so i want a solution for this problem. so i have decided to disable the dropdown until the data is not fully loaded so i make a variable in state with value false (first time dropdown is disabled) and when the data is loaded make it true. but problem is that i'm unable to update the state of parent from child.
i have create a function called
handleChange(e)
{
this.setState({ isDisabled: false });
}
and in my state i have set isDisabled value true. i.e means by default it is disable. In child component jqwidget data adapter in callback i'm calling the parent function.
let dataAdapter = new jqx.dataAdapter(currentComponent.state.source,{ formatData: function (data) { },
loadComplete: function (data)
{
this.props.handler(false)
//console.log(data);
}.bind(this)
i'm not putting my complete code you can check the snippets here.so how to update the parent state from child.

React Native - Getting current time and creating a dynamic x(time)-axis?

I just started working with React Native two weeks ago and I hit a wall.
I'm working on an IoT project that is logging things like temperature, humidity, CO levels etc..
I also decided to create an app to display these parameters inside a real-time chart. So far, I figured out how to communicate with the server and get data on demand and how to draw charts. The only problem is, how do I make the x-axis (time axis) dynamic? I want to update the graph every few seconds (let's say 5) when I make a new request for parameters to the server.
I've never done anything similar and I'm new to React so I have no idea how to handle this. I'd love if anyone could show a quick example in React Native with dynamic x-axis, even if it's with randomly generated numbers.
And oh, how do I get current time? I want it in a format like hours:minutes.
Thank you!
There are many ways to implement this depending on how you're getting data from the server and what library you're using to plot out data. I'll try to give as generic of an answer as I can.
Let's assume you have a function called getXAxisValueFromServer which will asynchronously request the value you want from your server and return it in a callback. (This is assuming your application queries data from the server periodically. If your application is reactive and receives data from the server through something like an Rx.js observable, the logic would be the same but you would put it in the observable's data callback)
To make your component render this dynamically, you need to make your component re-render whenever a new value arrives.
A React component re-renders under exactly two conditions: when its state changes, and when its props change.
Therefore, in order to have your component dynamically update, you'll need to tie your data to its state or its props.
In general, when using React, it's a good idea to keep presentation separated from logic. So the best approach to this would be to create a "smart" container component which receives data from the server and keeps that data in its state, and a "dumb" presentation component which renders the received data. Let's assume we have a chart which correctly plots data implemented as the Chart component, which receives a prop called data. We now want to implement a ChartContainer which will query the server for data periodically and update the Chart:
import React, { Component } from 'react';
import { getXAxisValueFromServer } from './server-api';
import Chart from './chart';
export default class ChartContainer extends Component {
constructor(props) {
super(props);
this.state = { data: null };
this.intervalHandle = null;
}
componentDidMount() {
this.intervalHandle = setInterval(() =>
getXAxisValueFromServer((data) => this.setState({ data })),
3000); //this is just an example - here we try to update
//the data every 3 seconds
}
render() {
return (
<Chart data={this.state.data} />
);
}
}
So here, we have created an example container which will query the server for new data every 3 seconds and update the chart with the received data when the server returns it. You should be able to adapt this to your use case.

What is the purpose of having a didInvalidate property in the data structure of a react-redux app's state?

I'm learning from the react-redux docs on middleware and have trouble understanding the purpose of the didInvalidate property in the reddit example. It seems like the example goes through the middleware to let the store now the process of making an API call starting with INVALIDATE_SUBREDDIT then to REQUEST_POSTS then to RECEIVE_POSTS. Why is the INVALIDATE_SUBREDDIT necessary? Looking at the actions below, I can only guess that it prevents multiple fetches from happening in case the user clicks 'refresh' very rapidly. Is that the only purpose of this property?
function shouldFetchPosts(state, subreddit) {
const posts = state.postsBySubreddit[subreddit]
if (!posts) {
return true
} else if (posts.isFetching) {
return false
} else {
return posts.didInvalidate
}
}
export function fetchPostsIfNeeded(subreddit) {
return (dispatch, getState) => {
if (shouldFetchPosts(getState(), subreddit)) {
return dispatch(fetchPosts(subreddit))
}
}
}
You are close that didInvalidate is related to reducing server requests, however it is kind of the opposite of preventing fetches. It informs the app it should go and fetch new data; the current data did 'invalidate'.
Knowing a bit about the lifecycle will help explain further. Redux uses mapStateToProps to help to decide whether to redraw a Component when the global state changes.
When a Component is about to be redrawn, because the state (mapped to the props) changes for instance, componentDidMount is called. Typically if the state depends on remote data componentDidMount checks to see if the state contains a current representation of the remote data (e.g. via shouldFetchPosts).
You are correct that it is inefficient to keep making the remote call but it is shouldFetchPosts that guards against this. Once the required data has been fetched (!posts is false) or it is in the process of being fetched (isFetching is true) then the check shouldFetchPosts returns false.
Once there is a set of posts in the state then the app will never fetch another set from the server.
But what happens when the server side data changes? The app will typically provide a refresh button, which (as components should not change the state) issues an 'Action' (INVALIDATE_SUBREDDIT for example) which is reduced into setting a flag (posts.didInvalidate) in the state that indicates that the data is now invalid.
The change in state triggers the component redraw which, as mentioned, checks shouldFetchPosts which falls into the clause that executes return posts.didInvalidate which is now true, therefore firing the action to REQUEST_POSTS and fetching the current server side data.
So to reiterate: didInvalidate suggests a fetch of the current server side data is needed.
The most up-voted answer isn't entirely correct.
didInvalidate is used to tell the app whether the data is stale or not. If true, the data should be re-fetched from the server. If false, we will use the data we already have.
In the official examples, firing INVALIDATE_SUBREDDIT will set didInvalidate to true. This Redux action can be dispatched as a result of a user action (clicking a refresh button), or something else (a countdown, a server push etc.)
However, firing INVALIDATE_SUBREDDIT alone will not initiate a new request to the server. It is simply used to determine whether we should re-fetch the data or use the existing data when we call fetchPostsIfNeeded().
Because didInvalidate is set to true, the app will not let us fetch the data more than once. To refresh our data (e.g. after clicking a refresh button) we need to:
dispatch(invalidateSubreddit(selectedSubreddit))
dispatch(fetchPostsIfNeeded(selectedSubreddit))
Because we called invalidateSubreddit(), didInvalidate is set to true and fetchPostsIfNeeded() will initiate a re-fetch.
(This is why danmux's answer isn't entirely correct. The life cycle method componentDidMount will not be called when the state (which is mapped to the props) changes; componentDidMount is only called when the component mounts for the first time. So, the effect of hitting the refresh button will not appear until the component has been remounted, e.g. from a route change.)

React.js - clean way to differentiate loading/empty states in Render

I have my component:
getInitialState() {
return {
items: []
};
},
componentDidMount() {
// make remote call to fetch `items`
this.setState({
items: itemsFromServer
})
},
render(){
if(!this.state.items.length){
// show empty state
}
// output items
}
Extremely contrived/sandboxed, but this is the general idea. When you first load this component, you see a flash of the "empty state" HTML, as the server hasn't yet returned any data.
Has anyone got an approach/a React Way™ of handling whether there is actually no data vs. showing a loading state?
I've just been rendering a empty span element but you could just as easily render a CSS spinner of some kind to show it's loading.
if(!this.state.items.length){
return(<div class="spinner-loader">Loading…</div>);
}
http://www.css-spinners.com/
You may also want to consider what happens if your response comes back with no results. I would use (this.state.items === null) to indicate that you are waiting for results and an empty array/collection (!this.state.items.length) to indicate that no results were returned.

Categories

Resources