You specified `onScroll` on a <ScrollView> but not `scrollEventThrottle` - reactjs

I am using Scrollview of react-native in my code.
<ScrollView style={styles.productlist} >
<CarouselComponent images={images}/>
</ScrollView>
Its working fine on an app, with no error, and no warning but in Console I am getting the below message repeatedly:
You specified onScroll on a but not scrollEventThrottle. You will only receive one event. Using 16 you get all the events but be aware that it may cause frame drops, use a bigger number if you don't need as much precision.
Don't know what is missing, I have not passed onScroll props but still getting this message.
Any suggestion...
My Carousel component Code is:
const renderImages = (image, index) => {
return (
<Image
key={index}
style={styles.carouselImage}
source={{ uri: image.large }}
/>
);
}
const CarouselComponent = (props) => {
const { images: { alternate = [] } = {} } = props;
if (alternate.length > 0) {
return (
<Carousel
delay={3000}
autoplay
style={styles.carouselLayout}
bullets
chosenBulletStyle={globalStyle.proDetCarSelectBullet}
bulletStyle={globalStyle.productDetailBullet}
bulletsContainerStyle={globalStyle.proDetCarBullet}
isLooped
currentPage={0}
>
{alternate.map((image, index) => renderImages(image, index))}
</Carousel>
);
}
return null;
}

You are probably using an older version of react-native-looped-carousel. On github there is a issue, which was fixed 27 days ago, describing your problem.
Fix:
Updating to the latest version of react-native-looped-carousel should resolve your issue. As an alternative you can fix the error by manually adding the scrollEventThrottle. See here.

There is nothing wrong in your code, the problem is contained into react-native-looped-carousel as an issue states here: https://github.com/phil-r/react-native-looped-carousel/issues/269
I suggest you to search for another library. React-native's code keep growing in a very fast way and every library should be updated frequently.
Just to learn something new, the scrollEventThrottle prop defines how many times the onScroll event will be fired while you scrolling. The bigger the number is, less times the event will be fired. 16 is most precise value.

Actually, the main ScrollView component needs the following code:
<ScrollView
scrollEventThrottle={16}
~~~

Related

react native Why does my async code hang the rendering?

General :
TL;DR: async code hangs rendering.
I have this component with a Modal and inside the Modal it renders a list of filters the user can choose from. When pressing a filter the color of the item changes and it adds a simple code(Number) to an array. The problem is that the rendering of the color change hangs until the logic that adds the code to the array finishes.
I don't understand why adding a number to an array takes between a sec and two.
I don't understand why the rendering hangs until the entire logic behind is done.
Notes: I come from a Vue background and this is the first project where I'm using react/react-native. So if I'm doing something wrong it would be much appreciated if someone points that out
Snack that replicates the issue :
Snack Link
My code for reference :
I use react-native with expo managed and I use some native-base components for the UI.
I can't share the whole code source but here are the pieces of logic that contribute to the problem :
Parent : FilterModal.js
The rendering part :
...
<Modal
// style={styles.container}
visible={modalVisible}
animationType="slide"
transparent={false}
onRequestClose={() => {
this.setModalVisible(!modalVisible);
}}
>
<Center>
<Pressable
onPress={() => this.setModalVisible(!modalVisible)}
>
<Icon size="8" as={MaterialCommunityIcons} name="window-close" color="danger.500" />
</Pressable>
</Center>
// I use sectionList because the list of filters is big and takes time to render on the screen
<SectionList
style={styles.container}
sections={[
{ title: "job types", data: job_types },
{ title: "job experience", data: job_experience },
{ title: "education", data: job_formation },
{ title: "sector", data: job_secteur }
]}
keyExtractor={(item) => item.id}
renderItem={({ item, section }) => <BaseBadge
key={item.id}
pressed={this.isPressed(section.title, item.id)}
item={item.name}
code={item.id}
type={section.title}
add={this.addToFilters.bind(this)}
></BaseBadge>}
renderSectionHeader={({ section: { title } }) => (
<Heading color="darkBlue.400">{title}</Heading>
)}
/>
</Modal>
...
The logic part :
...
async addToFilters(type, code) {
switch (type) {
case "job types":
this.addToTypesSelection(code);
break;
case "job experience":
this.addToExperienceSelection(code);
break;
case "formation":
this.addToFormationSelection(code);
break;
case "sector":
this.addToSectorSelection(code);
break;
default:
//TODO
break;
}
}
...
// the add to selection methods look something like this :
async addToTypesSelection(code) {
if (this.state.jobTypesSelection.includes(code)) {
this.setState({ jobTypesSelection: this.state.jobTypesSelection.filter((item) => item != code) })
}
else {
this.setState({ jobTypesSelection: [...this.state.jobTypesSelection, code] })
}
}
...
Child :
The rendering Part
render() {
const { pressed } = this.state;
return (
< Pressable
// This is the source of the problem and read further to know why I used the setTimeout
onPress={async () => {
this.setState({ pressed: !this.state.pressed });
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
<Badge
bg={pressed ? "primary.300" : "coolGray.200"}
rounded="md"
>
<Text fontSize="md">
{this.props.item}
</Text>
</Badge>
</Pressable >
);
};
Expected outcome :
The setState({pressed:!this.state.pressed}) finishes the rendering of the item happens instantly, the rest of the code happens after and doesn't hang the rendering.
The change in the parent state using the add code to array can happen in the background but I need the filter item ui to change instantly.
Things I tried :
Async methods
I tried making the methods async and not await them so they can happen asynchronously. that didn't change anything and seems like react native ignores that the methods are async. It hangs until everything is done all the way to the method changing the parent state.
Implementing "event emit-listen logic"
This is the first app where I chose to use react/react-native, coming from Vue I got the idea of emitting an event from the child and listening to it on the parent and execute the logic that adds the code to the array.
This didn't change anything, I used eventemitter3 and react-native-event-listeners
Using Timeout
This is the last desperate thing I tried which made the app useable for now until I figure out what am I doing wrong.
basically I add a Timeout after I change the state of the filter component like so :
...
< Pressable
onPress={async () => {
// change the state this changes the color of the item ↓
this.setState({ pressed: !this.state.pressed });
// this is the desperate code to make the logic not hang the rendering ↓
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
...
Thanks for reading, helpful answers and links to the docs and other articles that can help me understand better are much appreciated.
Again I'm new to react/react-native so please if there is some concept I'm not understanding right point me in the right direction.
For anyone reading this I finally figured out what was the problem and was able to solve the issue for me.
The reason the rendering was getting hang is because the code that pushes to my array took time regardless of me making it async or not it was being executed on the main thread AND that change was triggering screen re-render which needed to wait for the js logic to finish.
The things that contribute to the solution and improvement are :
Make the array (now a map{}) that holds the selected filters stateless, in other words don't use useState to declare the array, instead use good old js which will not trigger any screen re-render. When the user applies the filters then push that plain js object to a state or context like I'm doing and consume it, doing it this way makes sure that the user can spam selecting and deselecting the filters without hanging the interactions.
first thing which is just a better way of doing what I needed is to make the array a map, this doesn't solve the rerender issue.

Recording user interactions in React Native / Snap Carousel - firstItem

I have been having this issue for a couple of days and cannot seem to find a solution.
I would like to record the user's interaction on a database. I am displaying data using a React Native Snap Carousel, and need to record if the item was viewed by the user. To do this, and as described in other stack overflow questions, I am using "onSnapToItem". onSnapToItem is triggered everytime I change from one slide to another. Then, if the user views the slide for more than 2 seconds, I count that as an interaction.
The problem is that onSnapToItem is not triggered on the firstItem (which makes sense, because I am not changing slide). Can anybody think of a solution?
<Carousel
vertical
layout={"default"}
ref={_carousel}
data={promos}
renderItem={_renderItem}
autoplay={true}
autoplayInterval={4000}
onSnapToItem = { (index) => {
clearTimeout(writeDelay)
writeDelay=setTimeout (()=>{
console.log(promos[index].id)
},2000)
}}
onEndReached={_retrieveMore}
/>
Programmatically snap to item on screen focus.
componentDidMount() {
setTimeout(() => this._carousel.snapToItem(0), 1000);
// if above code doesn't work then you can try is for first item
writeDelay=setTimeout (()=>{
console.log(promos[0].id); // index here is 0 for first item
},1000)
}
...
render() {
<Carousel
...
ref={c => { this._carousel = c }}
/>
}`
you can use this library #svanboxel/visibility-sensor-react-native

How to fix image flickering by rerendering once and for all?

I'm trying to make a small sprite-based game with ReactJS. The green dragon (was taken from HMMII) is flying across the hexagonal field and it's behavior depends on mouse clicking. The sprites change each other with speed depending on a specially chosen time constant - 170ms. More precisely: there is a div representing the dragon and it's properties (top, left, width, height and background-image) always are changing.
At the first stage of the development I've faced with irritating blinking and flickering by rerendering the image. How can avoid it?
Below are described multiple ways I've used with some previews made with Surge. The strongest effect is watched in Google Chrome but in Firefox also are troubles.
0) At first I've tried to use CSS-animation based on #keyframes, but it was no good due to fade effect. And I don't need any fade effects at all, I need rapid rerendering.
1) This is the most straightforward attempt. After clicking on a particular field, componentWillReceiveProps is creating the list of steps and then all of this steps are performing consistently. Also I've tried to use requestAnimationFrame instead of setTimeout but with the same troubles.
makeStep() {
const {steps} = this.state;
this.setState((prevState, props) => ({
steps: steps.slice(1),
style: {...}
}));
}
render() {
const {steps, style} = this.state;
steps.length ? setTimeout(this.makeStep, DRAGON_RENDER_TIME):
this.props.endTurn();
return (<div id="dragon" style={style}></div>);
}
Here is the result: http://streuner.surge.sh/ As you can see, dragon is often disapearing by launching and landing, it fly with skipping some sprites.
2) I've tried to test method describen in article:
https://itnext.io/stable-image-component-with-placeholder-in-react-7c837b1ebee
In this case I've changed my div with background-image to other div containing explicit img. At first, this.state.isLoaded is false and new sprite will not appear. It appears only after the image has been loaded with onLoad method. Also I've tried to use refs with attempt watch for complete-property of the image but it's always true - maybe because size of the image is very small.
setLoaded(){
this.setState((prevState, props) => ({
isLoaded: true
}));
}
render() {
const {isLoaded, steps, style} = this.state;
if(isLoaded) {
steps.length ? setTimeout(this.makeStep, DRAGON_RENDER_TIME):
this.props.endTurn();
}
return (<div id="wrap" style={{top:style.top, left:style.left}} >
<img id="dragon" alt="" src={style.src} onLoad={this.setLoaded}
style={{width:style.width,
height: style.height,
visibility: isLoaded ? "visible": "hidden"}}/>
</div>);
}
Here is the result: http://streuner2.surge.sh/ There's no more sprite skipping but the flickering effect is much stronger than in first case.
3) Maybe it was my best attempt. I've read this advice: https://github.com/facebook/react-native/issues/981 and decided to render immediately all of the step images but only the one with opacity = 1, the others have opacity = 0.
makeStep(index) {
const {steps} = this.state;
this.setState((prevState, props) => ({
index: index + 1,
steps: steps.map( (s, i) => ({...s, opacity: (i !== index) ? 0: 1}))
}));
}
render() {
const {index, steps} = this.state;
(index < steps.length) ?
setTimeout(() => this.makeStep(index), DRAGON_RENDER_TIME):
this.props.endTurn();
return ([steps.map((s, i) =>
<div className="dragon" key={i} style={s}></div>)]);
}
It's possible to see the result here: http://streuner3.surge.sh/ There's only one flickering by starting new fly with rerendering all sprites. But the code seems to me more artificial.
I would like to emphasize that the behavior always depends on browser, in Firefox it's much better. Also there are differences with variety of flys in the same browser: sometimes there's no flickering effect but in most of cases it unfortunately is. Maybe I don't understand any basic notion of rerendering images in browser.
I think you should shift your attention from animation itself and pay more attention to rerendering in React, each time when you change Image component state or props it is rerendering. Read about lifecycle methods and rerendering in React docs.
You change state very fast(in your case it's almost 6 times per second), so I suppose that some of the browsers are not fast enough with Image component rerendering. Try to move out of Image state variables which updates so fast and everything will be ok
I know the answer is late, but posting my answer here in case someone still wants to find a solution and because I've found this drives some traffic.
A simple workaround is to add a CSS transition property to the image like the below:
transition: all .5s;
it does not prevent the image re-rendering, but at least it does prevent the image flickering.

React native performance issue

I am using coincap api's to first fetch Data of about 1500+ crypto currency and then Web-socket to update the updated value of crypto Currency.
I a using redux to manage my state here
Inside My componentDidMount(), I am calling a redux action fetchCoin which fetches the value of the coin
componentDidMount() {
this.props.fetchCoin()
}
And then In return I am doing something like this
<FlatList
data={this.state.searchCoin ? displaySearchCrypto : this.props.cryptoLoaded}
renderItem={({ item }) => (
<CoinCard
key={item["short"]}
coinShortName = {item["short"]}
coinName = {item["long"]}
coinPrice = {item["price"].toFixed(2)}
percentChange = {item["perc"].toFixed(2)}
/>
Then I have a web-socket which updates the value of cryptocurrency like this
componentDidUpdate() {
if (this.state.updateCoinData || this.updateCoinData.length < 1 ) {
this.updateCoinData = [...this.props.cryptoLoaded];
this.setState({updateCoinData: true})
}
this.socket.on('trades', (tradeMsg) => {
for (let i=0; i< this.updateCoinData.length; i++) {
if (this.updateCoinData[i]["short"] == tradeMsg.coin ) {
//Search for changed Crypto Value
this.updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
this.updateCoinData[i]["price"] = tradeMsg['message']['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(this.updateCoinData);
}
}
})
}
Now, While this work, the problem is that this is slowing my app like hell since whenever the socket sends new data, it has to render every component and hence events like touch and search takes lot of time to execute. [Update] It turns out my app is rendering something even If i remove socket connection, check out update 2
[Question:] What should I do so that I can improve the performance of App? (Something like not using state or using DOM to update my app and so on).
[Update 1:] I am using https://github.com/irohitb/Crypto
And these two are js files where all the logic is happening
https://github.com/irohitb/Crypto/blob/master/src/container/cryptoContainer.js
https://github.com/irohitb/Crypto/blob/master/src/components/CoinCard.js
I have also move from map to Flatlist.
[Update: 2] I found that there are endless render happening inside my App which is probably keeping my thread busy (I mean it is endless & unnecessarily passing props). I asked the same question on separate Stackoverflow thread but didn't received a proper response and since it is related to performance, I thought about putting a bounty on it here.
Please check this thread: infinite Render in React
[Answer Update:] While there are many great answers here, Just in case someone wants to understand how it worked, You could probably clone my repository and go back to before this commit. I have linked the commit to the point where my problems was solved (so you might need to go back and see what I was doing wrong). Also, All the answers were very useful and not hard to comprehend so you should definitely go through them.
Each time your component updates it starts a new socket which results in a memory leak and will cause this.props.updateCrypto(updateCoinData); to be called multiple times for the same data. This can be fixed by opening the socket in componentDidMount() and closing it in componentWillUnmount().
You can also buffer multiple record updates and change the FlatList data in one go every couple of seconds.
Edit, working example (App.js):
import React, { Component } from 'react';
import { Text, View, FlatList } from 'react-native';
import SocketIOClient from 'socket.io-client';
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.currencies = {};
this.state = {
currenciesList: [],
}
}
componentDidMount() {
this.socket = SocketIOClient('https://coincap.io');
this.socket.on('trades', (tradeMsg) => {
const time = new Date();
// Store updates to currencies in an object
this.currencies[tradeMsg.message.msg.short] = {
...tradeMsg.message.msg,
time: time.getHours() + ':' + time.getMinutes() + ':' + time.getSeconds(),
};
// Create a new array from all currencies
this.setState({currenciesList: Object.values(this.currencies)})
});
}
componentWillUnmount() {
this.socket.disconnect();
}
render() {
return (
<FlatList
data={this.state.currenciesList}
extraData={this.state.currenciesList}
keyExtractor={(item) => item.short}
renderItem={({item}) => <View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={{flex: 1}}>{item.time}</Text>
<Text style={{flex: 1}}>{item.short}</Text>
<Text style={{flex: 1}}>{item.perc}</Text>
<Text style={{flex: 1}}>{item.price}</Text>
</View>}
/>
);
}
}
There're many standard ways to improve react app performance, the most common:
use usual react optimizations (shouldComponentUpdate, PureComponent - read docs)
use virtual lists (limit visible parts of data)
In this case I would add:
Don't process data before optimizations - f.e. formatting data that didn't changed is at least unnecessary. You can insert intermediate component (optimization layer) that will pass/update formatted data into <CoinCard /> only on 'raw data' change.
You might not need Redux at all (store data in state) when data is used in one place/simple structure. Of course you can use redux for other globally shared app state (f.e. filtering options).
Use <FlatList /> (react-native), search for sth more suitable?
UPDATE
Some code was changed in mean time (repo), at this time (08.09) one issue still exist and probably causing memory leaks.
You're calling this.socket.on on each componentDidUpdate call (wrongly coded conditions) - continuously adding a new handler!
componentDidUpdate() {
// call all ONLY ONCE afer initial data loading
if (!this.state.updateCoinData && !this.props.cryptoLoaded.length) {
this.setState({updateCoinData: true}) // block condition
this.socket.on('trades', (tradeMsg) => {
// slice() is faster, new array instance
// let updateCoinData = [...this.props.cryptoLoaded];
let updateCoinData = this.props.cryptoLoaded.slice();
for (let i=0; i<updateCoinData.length; i++) {
//Search for changed Crypto Value
if (updateCoinData[i]["short"] == tradeMsg.coin ) {
// found, updating from message
updateCoinData[i]["long"] = tradeMsg["message"]["msg"]["long"]
updateCoinData[i]["short"] = tradeMsg["message"]["msg"]["short"]
updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
updateCoinData[i]["mktcap"] = tradeMsg['message']['msg']["mktcap"]
updateCoinData[i]["price"] = tradeMsg['message']['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(updateCoinData);
// record found and updated, no more looping needed
break;
}
}
})
}
}
Minor errors: initial fetching states set to true in reducers.
Searching for performance issues I would look at <CoinCard />:
make it PureComponent;
increased and decreased aren't required to be saved at state which forces unnecessasry render calls;
I would use update time (not saved in state, just passed as prop in parent and only for updated rows, within updateCoinData in code above) and derive direction (check for 0 and sign only) of difference (already calculated in perc) only for visible items (from render) and only during time limit (difference between render time and data update prop). setTimeout can be used, too.
finally removing componentWillReceiveProps, componentDidUpdate and shouldComponentUpdate should (highly?) improve performance;
Like Bhojendra Rauniyar said, you should use shouldComponentUpdate in CoinCard. You probably also want to change your FlatList, your downsized sample has the FlatList in a ScrollView, this causes the FlatList to fully expand, thus rendering all it's items at once.
class cryptoTicker extends PureComponent {
componentDidMount() {
this.socket = openSocket('https://coincap.io');
this.props.fetchCoin()
this.props.CurrencyRate()
this.socket.on('trades', (tradeMsg) => {
for (let i=0; i< this.updateCoinData.length; i++) {
if (this.updateCoinData[i]["short"] == tradeMsg.coin ) {
//Search for changed Crypto Value
this.updateCoinData["short"] = tradeMsg["message"]["msg"]["short"]
this.updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
this.updateCoinData[i]["price"] = tradeMsg["message"]['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(this.updateCoinData);
}
}
})
}
componentWillReceiveProps(newProps){
// Fill with redux data once
if (this.updateCoinData.length < 1 && newProps.cryptoLoaded) {
this.updateCoinData = [...newProps.cryptoLoaded];
}
}
render() {
return (
<View style={{height: '100%'}}>
<Header/>
<FlatList
style={{flex:1}}
data={this.props.cryptoLoaded}
keyExtractor={item => item.short}
initialNumToRender={50}
windowSize={21}
removeClippedSubviews={true}
renderItem={({item, index}) => (
<CoinCard
index={index}
{...item}
/>
)}
/>
</View>
)
}
}
class CoinCard extends Component {
shouldComponentUpdate(nextProps) {
return this.props.price !== nextProps.price || this.props.perc !== nextProps.perc
}
render() {
console.log("here: " + this.props.index);
return (
<View>
<Text> {this.props.index} = {this.props.long} </Text>
</View>
)
}
}
When rendering Flatlist you should consider using PureComponent or utilizing shouldComponentUpdate hook to update only if required.
From the doc:
If your application renders long lists of data (hundreds or thousands of rows), we recommended using a technique known as “windowing”. This technique only renders a small subset of your rows at any given time, and can dramatically reduce the time it takes to re-render the components as well as the number of DOM nodes created.
Take a deep drive to this performance guide.
If you still want some advanced look through, then I will recommend you to look into the following threads:
FlatList and VirtualizedList Scroll performance is laggy after 30+ rows
Performance problems with react when using a big list
You should never do API calls in React's componentWillMount() lifecycle method, instead it should be done in componentDidMount().
Check out this very neat article on lifecycle methods and what should be done in which method: https://medium.com/#baphemot/understanding-reactjs-component-life-cycle-823a640b3e8d.
Many will be tempted to use this function in order to send a request to fetch data and expect the data to be available before the initial render is ready. This is not the case — while the request will be initialized before the render, it will not be able to finish before the render is called.
Instead of creating a socket to update the coinData, you might want to make use of redux subscibe/unsubscribe methods.

React hide unknown-prop warning

I'm using a package which has an issue that causes React to console the error "unknown-prop warning", i am looking for a short-term solution to hide the error until something else can be found/used.
Is there a way to hide this?
edit: its more of a general question as to how to overall disable these errors, i want to avoid rollbar flagging it as it will cause a lot of errors in the logs.
but im using reactslick, theres numerous reported issues of this error but i never found a fix, it comes from the code snippet below: ( i believe it passes in every prop even the ones it doesnt need.
SliderButtonNext = () =>
<button className="react-slick-slider">
<FaChevronRight size={24} className="arrow" />
</button>
SliderButtonPrev = () => {
<button className="react-slick-slider">
<FaChevronLeft size={24} className="arrow" />
</button>
}
const settings = {
nextArrow: this.SliderButtonNext(),
prevArrow: this.SliderButtonPrev(),
};

Resources