react-native flatlist detect when feed is loaded and rendered - javascript

I am implementing a react-native newsfeed with FlatList and I would like to detect then the feed is loaded, and the first few items rendered. The idea is that the splash page would be shown until the the newsfeed is rendered (https://facebook.github.io/react-native/blog/2018/01/18/implementing-twitters-app-loading-animation-in-react-native.html), at which point the splash feed would animate to the newsfeed. The question is where I do detect the isRendered event. My FlatList has two props that may be of interest:
class NewsFeed extends Component {
state = { data: [] }
loadData = async () => { ... }
renderItem = ({ item }) => { ... }
render () {
return <FlatList renderItem = {this.renderItem} data={this.state.data} />
}
}
I tried flipping the the isRendered boolean flag as a last line in loadData, but after transitioning from the landing screen, it still takes a fraction of a second (sometimes) before the pictures render. So I flipped the isRendered boolean flag in renderItem function, and we never advance past the splash screen at all, meaning the function is never run.

You might want to check the value of isRendered in the render function. If isRendered is still false, you display the splash screen. Then if the boolean is true, you can begin the animation from this component and display the list right away as the data is already loaded. To make this work, call setState to flip the isRendered boolean in loadData as you were doing before, so the render function gets called again once the data is loaded. (And you can call the loadData function in componentDidMount as this will be called right away when the component gets mounted).
If the animation is being called from a different component, you might want to load the data there and do the isRendered check in that component's render function, and then possibly pass the data as a prop to make sure the NewsFeed component is already loaded with the data.

Related

How can I cache or memoize useQuery results from apollo client in order to stop re rendering of the component when its props have not changed

Say I have my component structure in this manner:
function Parent Component(){
// radio scroll is responsible for detecting the scroll on the page true indicates it is below a certain height false means its on top
const {radioScroll} = useCustomContextHook()
function handleColorChange(){
setColor(blue)
}
return (<div style={{backgroundColor: {radioScroll ? 'blue' : 'green'}}}>
<ChildComponent />
</div>)
}
function ChildComponent(props){
const {data,loading,error} = useQuery(query,variables)
if (error) console.log(error)
if (loading) return 'loading..'
if (data){
return <ChartJSComponent data={data} />
}
}
in the above code block, say radio scroll is coming from a higher order context which tells me when i scroll down if true we are on top of page if false we are below certain number of pixels.
based on which i change the parent component styling, now this radio scroll is not used in my child component however the usequery operation is expensive and time taking since i use a webhook as proxy to authenticate hence the child component always shows a loader when scrolling down meaning the child component re evaluates whenver the radioScroll state changes, i have tried the below but no luck.
export default memo(ChildComponent)
tried to use the memo on the child component to avoid the re evaluation/rendering, but it wasnt working for me.
im using react18, i have tried to wrap the useQuery inside a useMemo, but its throwing errors that i cannot wrap a custom hook inside it.
Thanks!

Render loading screen while waiting for DOM to render after update

I want to render a loading screen while the render() function of my React app is running. This question solves the problem for the initial loading of the application, however I need to perform updates to the app state and re-render the application afterward based on the new state, which appears to be very costly. However, anything I try causes the app to freeze entirely while the render is running. Is there a way to render a dynamic loading screen while waiting for the DOM to re-render?
Here is a codesandbox showing my intentions: https://codesandbox.io/s/elated-lamarr-fhqwe?file=/src/App.js
In the example, I would like for the "LOADING" div to show (ideally this would be some sort of dynamic "loader" component) while the other divs are being rendered, and then disappear when the render is done.
You have conditional-rendering for this purpose
...
this.state = {
isLoading: true
};
...
componentDidMount() {
expensiveCalculation(...);
this.setState({ isLoading: false }); // Add additional state updates as per above results
}
...
render() {
this.state.isLoading ? <Loader /> : <Component {...props} />;
}
If the computation is expensive, you can use the libraries like memoize-one which helps you to skip the intensive calculations if there is no change in input values.

How to re-render a React Component Upon re-direct

I am creating a clone of SoundCloud, and when I delete a song from the song page, I want to re-direct to the song index component (the /discover page).
I am able to successfully re-direct there upon deletion, but the index page appears empty until I refresh, whereupon the songs are fetched. I've tried using a ComponentDidUpdate on the index page to fetchSongs again when the history.location is different, but haven't had success.
//Song Delete Component
handleDelete() {
this.props.removeSong(this.props.songId)
this.props.history.push("/discover");
}
//Song Index Component
componentDidUpdate(prevProps) {
if (this.props.history.location.pathname !==
prevProps.location.pathname) {
this.props.fetchSongs();
}
}
When your redirect to a new component/route, that component gets re-mounted to the screen. You should use componentDidMount() instead;
Discover Component:
componentDidMount() {
this.props.fetchSongs();
}
Edit ---
Just learned that your second page is actually a modal and that you execute your action-creatorand redirect in there. In that case, the Discover component may have not actually unmount from the screen, hence why componentDidMount() does not execute. But componentDidUpdate() should still work, let's refactor it a bit
So in Discover Component, try something like:
componentDidUpdate(prevProps){
if(this.props.songsOrNameOfReducerKey !== prevProps.songsOrNameOfReducerKey){
this.props.fetchSongs()
}
}
this.props.songsOrNameOfReducerKey just refers to the redux-state that holds the updated list of songs - available as a prop in your component. We compare the updated list to our previous list, and if there is a difference, we execute your action-creator.
Make sure you connected redux to your component via mapStateToProps() to get a hold of this state-value. Replace songsOrNameOfReducerKey with the key that you defined.
When you delete the song from your list, it should give the Discover component, new props, thus triggering componentDidUpdate()

Reactjs render component only when ALL its child done thier render

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

How to reset state on props change in an already mounted component?

I have a <BlogPost> component which could've been a Stateless Function Component, but turned out as a Class Stateful Component because of the following:
The blogPost items that it renders (receiving as props) have images embedded in their html marked content which I parse using the marked library and render as a blog post with images in between its paragraphs, h1, h2, h3, etc.
The fact is that I need to preload those images before rendering the post content to my client. I think it's a UX disaster if you start reading a paragraph and all of a sudden it moves down 400px because the image that was being loaded has been mounted to the DOM during the time you were reading it.
So I prefer to hold on by rendering a <Spinner/> until my images are ready. That's why the <BlogPost> is a class component with the following code:
class BlogPost extends React.Component {
constructor(props) {
super(props);
this.state={
pending: true,
imagesToLoad: 0,
imagesLoaded: 0
};
}
preloadImages(blogPostMedia) {
this.setState({
pending: true,
imagesToLoad: 0,
imagesLoaded: 0
});
... some more code ...
// Get images urls and create <img> elements to force browser download
// Set pending to false, and imagesToLoad will be = imagedLoaded
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props !== nextProps) {
this.preloadImages(nextProps.singleBlogPost.media);
}
}
componentDidMount() {
this.preloadImages(this.props.singleBlogPost.media);
}
render() {
return(
this.state.pending ?
<Spinner/>
: (this.state.imagesLoaded < this.state.imagesToLoad) ?
<Spinner/>
: <BlogPostStyledDiv dangerouslySetInnerHTML={getParsedMarkdown(this.props.singleBlogPost.content)}/>
);
}
}
export default BlogPost;
At first I was calling the preloadImages() only inside the componentDidMount() method. And that works flawlessly for the first post I render with it.
But as soon as I would click on the next post link; since my <BlogPost>component is already mounted, componentDidMount() doesn't get called again and all the subsequent posts I would render by clicking on links (this is a Single Page App) wouldn't benefit from the preloadImages() feature.
So I needed a way to reset the state and preload the images of the new blogPost received as props inside an update cycle, since the <BlogPost> component it's already mounted.
I decided to call the same preloadImages() function from inside the UNSAFE_componentWillReceiveProps() method. Basically it is reseting my state to initial conditions, so a <Spinner/> shows up right away, and the blog post only renders when all the images have been loaded.
It's working as intended, but since the name of the method contains the word "UNSAFE", I'm curious if there's a better way to do it. Even though I think I'm not doing anything "unsafe" inside of it. My component is still respectful to its props and doesn't change them in anyway. It just been reset to its initial behavior.
RECAP: What I need is a way to reset my already mounted component to its initial state and call the preloadImages() method (inside an update cycle) so it will behave as it was freshly mounted. Is there a better way or what I did is just fine? Thanks.
I would stop using componentWillReceiveProps()(resource). If you don't want the jarring effect, one way you can avoid it is to load the information from <BlogPost/>'s parent, and only once the information is loaded, to pass it into <BlogPost/> as a prop.
But anyway, you can use keys to reset a component back to its original state by recreating it from scratch (resource).
componentWillReceiveProps is deprecated, it's supposed to be replaced with either getDerivedStateFromProps or componentDidUpdate, depending on the case.
Since preloadImages is asynchronous side effect, it should be called in both componentDidMount and componentDidUpdate:
componentDidMount() {
this.preloadImages(this.props.singleBlogPost.media);
}
componentDidUpdate() {
this.preloadImages(this.props.singleBlogPost.media);
}

Categories

Resources