React Native Component will receive is deprecated - reactjs

I'm creating a new react native app after writing some code I got this warning :
in the emulator.
But I don't see where the problem is.
This is my code - App.js:
const IS_ANDROID = Platform.OS === 'android';
const SLIDER_1_FIRST_ITEM = 1;
class App extends Component {
constructor (props) {
super(props);
this.state = {
slider1ActiveSlide: SLIDER_1_FIRST_ITEM
};
}
_renderItem ({item, index}) {
return <SliderEntry data={item} even={(index + 1) % 2 === 0} />;
}
_renderItemWithParallax ({item, index}, parallaxProps) {
return (
<SliderEntry
data={item}
even={(index + 1) % 2 === 0}
parallax={true}
parallaxProps={parallaxProps}
/>
);
}
_renderLightItem ({item, index}) {
return <SliderEntry data={item} even={false} />;
}
_renderDarkItem ({item, index}) {
return <SliderEntry data={item} even={true} />;
}
mainExample (number, title) {
const { slider1ActiveSlide } = this.state;
return (
<View style={styles.exampleContainer}>
<Carousel
ref={c => this._slider1Ref = c}
data={ENTRIES1}
renderItem={this._renderItemWithParallax}
sliderWidth={sliderWidth}
itemWidth={itemWidth}
hasParallaxImages={true}
firstItem={SLIDER_1_FIRST_ITEM}
inactiveSlideScale={0.94}
inactiveSlideOpacity={0.7}
// inactiveSlideShift={20}
containerCustomStyle={styles.slider}
contentContainerCustomStyle={styles.sliderContentContainer}
loop={true}
loopClonesPerSide={2}
autoplay={true}
autoplayDelay={4000}
autoplayInterval={3000}
onSnapToItem={(index) => this.setState({ slider1ActiveSlide: index }) }
/>
</View>
);
}
get gradient () {
return (
<LinearGradient
colors={[colors.background1, colors.background2]}
startPoint={{ x: 1, y: 0 }}
endPoint={{ x: 0, y: 1 }}
style={styles.gradient}
/>
);
}
render () {
const example1 = this.mainExample(1);
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<StatusBar
translucent={true}
backgroundColor={'rgba(0, 0, 0, 0.3)'}
barStyle={'light-content'}
/>
{ this.gradient }
<ScrollView
style={styles.scrollview}
scrollEventThrottle={200}
directionalLockEnabled={true}
>
{ example1 }
</ScrollView>
</View>
</SafeAreaView>
);
}
}
export default App;
All I used is this carousel library https://github.com/archriss/react-native-snap-carousel nothing else but I don't know what I am doing wrong in this case
and is it really the code isn't going to work in the future ?

As said by the warning, componentWillReceiveProps is deprecated.
The component react-native-snap-carousel use that feature and is deprecated in the latest version of react-native.
You have to either change the node_modules/react-native-snap-carousel to use componentDidUpdate, use another component for carousel or disable the warning until the carousel maintainers updates their package.
To disable it you can do, inside you App.js, in the constructor:
import {YellowBox} from 'react-native'; //import it
YellowBox.ignoreWarnings(['Warning: componentWillReceiveProps']);
Hope this helps you!
EDIT.
About the other Warning you've got. That happens when you do a setState when a component has already been unmounted. Make sure that recreates that scenario. I would suggest to not ignore this warning using YellowBox but solve it.

Related

Perfomance problem with Flatlist in React native

What I want
I'm trying to use a FlatList to render an array of objects that comes from server(I'm using Apollo and Graphql to get data from server), but I'm getting a message from console:
VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. {"contentLength": 2392, "dt": 4527, "prevDt": 2777}
I followed the message and wrapped my renderItem in React.memo, I'm using functional components so I cant use PureComponent or shouldComponentUpdate, but after this the problem persist.
What I have
Component structure
HomeScreen (responsible for obtaining the data from the server)
└── ProductList (FlatList Wrapper)
└── ProductListItem (renderItem)
HomeScreen.js
// HomeScreen.js
function HomeScreen({navigation}) {
const {data} = useHomeScreenProductsQuery({}) // this hook get data from server with apollo useQuery method
// just print something on console but it can do something more complex
const handleOnPressItem = () => console.log('go to product')
return (
<ProductList
horizontal
numColumns={2}
products={data?.collection?.products?.edges.map(e => e.node) ?? []}
onPressItem={handleOnPressItem}
/>
)
}
ProductList.js
// ProductList.js
import React from 'react'
import {Dimensions, FlatList, View} from 'react-native'
import {ProductListItem} from '../ProductListItem'
function ProductList({
products,
horizontal = false,
numColumns = 1,
onPressItem = () => {},
}) {
const getLayoutManager = () => {
const layoutManager = {}
if (horizontal) {
layoutManager.horizontal = true
layoutManager.showsHorizontalScrollIndicator = false
layoutManager.ItemSeparatorComponent = ProductListItemSeparator
layoutManager.contentContainerStyle = {padding: spacing.base}
} else {
layoutManager.numColumns = numColumns
}
return layoutManager
}
const renderProductListItem = ({item}) => {
const columns = horizontal ? 2 : numColumns
return (
<ProductListItem
product={item}
itemContainerStyle={{
width: dimens.common.WINDOW_WIDTH / columns,
}}
columnWrapperStyle={{flexDirection: 'column'}}
onPressItem={onPressItem}
/>
)
}
return (
<FlatList
{...getLayoutManager()}
// Pass a changing value to the `key` prop to update the columns number on the fly
key={numColumns}
data={products}
renderItem={renderProductListItem}
/>
)
}
ProductListItem.js
// ProductListItem.js
function ProductListItem({
product,
itemContainerStyle = {},
columnWrapperStyle = {},
onPressItem = () => {},
}) {
const price = product.pricing?.priceRange?.start
const priceUndiscounted = product.pricing?.priceRangeUndiscounted?.start
const getProductPrice = () => {
if (equal(price, priceUndiscounted)) {
// render a price formated with $ symbol
return <TaxedMoney taxedMoney={price} defaultValue={0} category="h6" />
}
return (
<>
<View style={styles.priceContainer}>
<Text category="c2" appearance="hint">
Antes
</Text>
<TaxedMoney
taxedMoney={priceUndiscounted}
category="c2"
appearance="hint"
style={styles.priceUndiscounted}
/>
</View>
<TaxedMoney taxedMoney={price} category="h5" />
</>
)
}
const renderItemHeader = () => (
<View style={styles.itemHeader}>
// this is only a wrapper of react-native-fast-image library
<Thumbnail source={product} />
</View>
)
return (
<View style={itemContainerStyle}>
// component from #UI-kitten components library
<Card
onPress={onPressItem}
header={renderItemHeader}
style={[styles.row, columnWrapperStyle]}
>
{/*Card Body*/}
<Text
category="c1"
appearance="hint"
ellipsizeMode="tail"
numberOfLines={1}
>
{product.category?.name}
</Text>
<Text category="c1" ellipsizeMode="tail" numberOfLines={2}>
{product.name}
</Text>
{getProductPrice}
</Card>
</View>
)
}
export default React.memo(ProductListItem)

Auto focus Input within React Native Viewpager

I'm using a React Native Viewpager to take in user entry, and move to the next page on button press. Important to note that moving to the next page happens on button press, and not by normal scrolling, which is disabled.
The best way I could think to handle this was to have a state on the ViewPager, which would propagate into the child Entries.
ViewPager.tsx:
export default function ViewPager({ route, navigation }) {
const ref: React.RefObject<ViewPager> = React.createRef();
const [currentPage, setCurrentPage] = useState(0);
let setEntryPage = (page: number) => {
ref.current?.setPage(page);
setCurrentPage(page);
}
return (
<View style={{flex: 1}}>
<ViewPager
style={styles.viewPager}
initialPage={0}
ref={ref}
scrollEnabled={false}
>
{
GlobalStuff.map((entry, index) => {
return (
<Entry
key={index}
index={index}
pagerFocusIndex={currentPage}
pagerLength={quizDeck?.litems.length!}
setEntryPage={setEntryPage}
/>
)
})
}
</ViewPager>
</View>
);
};
Entry.tsx:
export function Entry(props: EntryProps) {
const inputRef: React.RefObject<Input> = React.createRef();
if (props.pagerFocusIndex === props.index) {
inputRef.current?.focus();
}
return (
<View>
<Input
// ...
ref={inputRef}
/>
<IconButton
icon="arrow-right-thick"
color={colorTheme.green}
onPress={() => {
props.index !== props.pagerLength - 1 ?
props.setEntryPage(props.index + 1) :
props.navigation!.reset({ index: 0, routes: [{ name: recapScreenName as any }] });
}}
/>
// ...
Unfortunately, inputRef appears to be null, and there is probably a better way of achieving what I'm trying to achieve anyway.
Anything in your render loop will be called every time the component renders.
// This is called on every render
const inputRef: React.RefObject<Input> = React.createRef();
// So is this, it's always null
if (props.pagerFocusIndex === props.index) {
inputRef.current?.focus();
}
Put side effects in effects.
// Untested
const inputRef = useRef();
useEffect(() => {
if (props.pagerFocusIndex === props.index) {
inputRef.current?.focus();
}
}, [inputRef.current, props.pagerFocusIndex, props.index]);

How could I structure this React Native Section List implementation

So I want to use RN Section list in a sort of unorthodox way.
I want the section list to pass off rendering to a component as the renderings won't be very uniform.
I want to use section list so as you scroll you still get to see the headers.
I made a component that takes in children and renders them in a section list like so:
class SomeSectionList extends Component {
render() {
let sections = React.Children.map(this.props.children, (Child, index) => {
return {title: Child.type.title, data: [''], renderItem: () => Child, index }
});
return (
<SectionList
renderSectionHeader={({section}) => {
return <Text style={{ fontWeight: "bold" }}>{section.title}</Text>
}}
sections={sections}
keyExtractor={(item, index) => item + index}
/>
);
}
}
And the usage would be something like:
<SomeSectionList>
<Comp1 />
<Comp2 />
</SomeSectionList>
However, my issue is. Say in this case Comp1 does not render anything from it's component, I want to be able to hide it's section from the section list.
How could the SomeSectionList component know that it didn't render anything or didn't have the data to render anything so it can hide it's section and it's header?
Any suggestions would be great. I feel like using SectionList for this is overkill (but it makes showing the headers nicer) so open to alternatives as well.
You can accomplish this using onLayout method that comes with View.
By which we can get the height of the component rendered. if it is 0 that means nothing is rendered inside it or else it contains some data.
See this Working example on snack
export default class App extends React.Component {
render() {
return (
<SomeSectionList>
<Comp1 />
<Comp2 />
<Comp1 />
<Comp2 />
<Comp1 />
</SomeSectionList>
);
}
}
class Comp1 extends React.Component {
render() {
return (
<View>
<Text>Comp11</Text>
</View>
);
}
}
class Comp2 extends React.Component {
render() {
return null;
}
}
class SomeSectionList extends React.Component {
constructor(props) {
super(props);
this.state = {
children: this.props.children,
};
}
onLayout = (event, index) => {
if (event.nativeEvent.layout.height <= 0) {
let oldProps = this.state.children;
oldProps.splice(index, 1);
this.setState({ children: oldProps });
}
};
render() {
let sections = React.Children.map(this.state.children, (Child, index) => {
return {
title: Child.type.title,
data: [''],
renderItem: () => (
<View onLayout={event => this.onLayout(event, index)}>
{this.state.children[index]}
</View>
),
index,
};
});
return (
<SectionList
renderSectionHeader={({ section }) => {
return <Text style={{ fontWeight: 'bold' }}>{section.title}</Text>;
}}
sections={sections}
keyExtractor={(item, index) => item + index}
/>
);
}
}
Here, first of all, we have assigned this.props.children into state. Then in onLayout method, we are checking if the current indexed child has 0 height or not. if yes then remove it from the array of children.
You'll see clearly that some views are deleting. for this thing what we have done in one scenario is put one loader that covers the whole SectionList area with position absolute and you can hide it when all things are rendered correctly.
Here is an alternate that I am thinking after the helpful advice of #JaydeepGalani!!
class SomeSectionList extends Component {
constructor(props) {
super(props)
this.state = {
hiddenChildren: {}
}
}
onLayout = (event, index) => {
if (event.nativeEvent.layout.height <= 0) {
const hiddenChildren = this.state.hiddenChildren
hiddenChildren[index] = true
this.setState({
hiddenChildren
})
} else {
const hiddenChildren = this.state.hiddenChildren
delete hiddenChildren[index]
this.setState({
hiddenChildren
})
}
}
render() {
let sections = React.Children.map(this.props.children, (Child, index) => {
return {
title: Child.type.title,
index,
data: [''],
renderItem: () => (
<View onLayout={event => this.onLayout(event, index)}>
{this.state.children[index]}
</View>
)}
});
return (
<SectionList
renderSectionHeader={({section}) => {
const index = section.index
if (this.state.hiddenChildren[index]) return
return <Text style={{ fontWeight: "bold" }}>{section.title}</Text>
}}
sections={sections}
keyExtractor={(item, index) => item + index}
/>
);
}
}
Since in the first implementation once the section got removed it's really hard to bring it back as the onLayouts don't get triggered. In this we still technically 'render' the section, but hide the header and since the section is of height 0 it won't show up, but it's still rendered and say at a later point in time that section changes and suddenly renders something it will now show up in the section list.
Curious on any feedback around this?

React Component Props are receiving late. (Meteor JS)

I am working on a react-native and meteor js project.
My problem is that the props received from withTracker() function are only received in componentDidUpdate(prevProps) I don't get them in constructor or componentWillMount.
Another issue is when i pass props directly from parent to child. it receives them late due to which my component does not update
iconGroups prop comes from withTracker() method
and openSection props which i am using in this showGroupIcons()
is passed directly from parent to this component.
I want to open Accordian section that is passed to it via parent. but problem is in componentDidUpdate(prevProps) I am changing state due to which component re-renders.
openSection variable by default value is Zero. when props arrvies it value changes which i required But Accordian does not update.
Below is my code
import React, { Component } from 'react';
import Meteor, { withTracker } from 'react-native-meteor';
import {
View, Image, ScrollView, TouchableOpacity,
} from 'react-native';
import PopupDialog from 'react-native-popup-dialog';
import {Text, Icon, Input, Item, List,} from 'native-base';
import Accordion from 'react-native-collapsible/Accordion';
import { Col, Row, Grid } from 'react-native-easy-grid';
import styles from './styles';
import CONFIG from '../../config/constant';
import {MO} from "../../index";
const staticUrl = '../../assets/img/icons/';
class IconPickerComponent extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
itemName: 'apple1',
activeSections: 0,
showAccordian: true,
accordianData: []
};
}
componentDidUpdate(prevProps) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(null);
}
method() {
// this.setState(...this.state,{
// searchText: ''
// })
this.iconPicker.show(); // show icon picker
}
onSearchChange(text) {
this.setState({
showAccordian: !(text.length > 0)
});
const searchText = text.toLowerCase();
const filteredItems = this.state.dataSource.filter((item) => {
const itemText = item.name.toLowerCase();
return itemText.indexOf(searchText) !== -1;
});
this.setState({ filteredItems });
}
onIconSelect(item) {
this.setState({
itemName: item,
});
this.iconPicker.dismiss();
if (this.props.onIconChanged) {
this.props.onIconChanged(item);
}
}
_renderSectionTitle = section => {
return (
<View style={styles.content}>
<Text></Text>
</View>
);
};
_renderHeader = section => {
return (
<View style={styles.accordHeader}>
<Text style={{color: 'white'}}>{this.state.showAccordian} - {section.group}</Text>
<Text>
<Icon style={styles.downArrow} name="ios-arrow-down" />
</Text>
</View>
);
};
_renderContent = section => {
return (
<View style={styles.accordContent}>
{
section.images.map((img, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(img)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: CONFIG.ICON_URL+ img + '.png'}}/>
</View>
</TouchableOpacity>
))
}
</View>
);
};
_updateSections = activeSections => {
this.setState({ activeSections });
};
hasGroupIcons() {
return this.props.iconGroups.length > 0;
};
showGroupIcons() {
if(this.state.showAccordian){
let openSection;
if(!!this.props.openSection) {
let groupIndex = this.state.accordianData.findIndex(icon => icon.group === this.props.openSection);
if(groupIndex !== -1) {
openSection = groupIndex;
} else {
openSection = 0;
}
} else {
openSection = 0;
}
return(<Accordion
sections={this.state.accordianData}
activeSections={this.state.activeSections}
renderSectionTitle={this._renderSectionTitle}
renderHeader={this._renderHeader}
renderContent={this._renderContent}
onChange={this._updateSections}
initiallyActiveSection={openSection} />);
} else {
return(<View style={{flexWrap: 'wrap', flexDirection: 'row'}}>
{
this.state.filteredItems.map((item, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(item.name)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: item.icon}}/>
</View>
</TouchableOpacity>
))
}
</View>)
}
};
render() {
return (
<PopupDialog
overlayOpacity={0.8}
overlayBackgroundColor="#414141"
dialogStyle={styles.dialogBox}
containerStyle={styles.dialogContainer}
ref={(popupDialog) => { this.iconPicker = popupDialog; }}
>
<ScrollView>
<View style={styles.dialogInner}>
<Item searchBar rounded style={styles.searchbar}>
<Icon style={styles.searchIcon} name="search" />
<Input onChangeText={this.onSearchChange.bind(this)} style={styles.inputSearch} placeholder="Search" />
</Item>
{
this.hasGroupIcons() && this.showGroupIcons()
}
</View>
</ScrollView>
</PopupDialog>
);
}
}
export default withTracker(params => {
MO.subscribe('ipSubsId3', 'IconGroups');
return {
iconGroups: MO.collection('IconGroups', 'ipSubsId3').find({}),
};
})(IconPickerComponent);
I am new to react. I am assuming when props change component re-renders.
Use this life cycle method
static getDerivedStateFromProps(prevProps, prevState) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
Read more about this lifecycle method here
I have fixed this issue. Actually my concepts were not right. I thought props are first received in constructor and componentWillMount. But I get all props in render() and everything works fine i dont have to use any lifecycle method to use props now

React Native / React navigation, same level component

I have 2 classes: my default class HomeScreen used for the home page and another class MyList which I use to generate a flatlist on my HomeScreen.
My problem is that I do not succeed in building my navigation function in my MyList class.
I always get the following error: "Can't find variable: navigate".
I took a look at this Calling Navigate on Top Level Component but I really don't know how to implement it in my code.
Here's what I've tried:
class MyList extends React.Component {
_keyExtractor = (item, index) => item.note.id;
_renderItem = ({ item }) => (
<TouchableNativeFeedback
onPress={() => navigate('Note', { noteId: item.note.id })} >
<View>
<Text style={styles.noteElementTitle} >{item.note.title}</Text>
<Text style={styles.noteElementBody} >{item.note.body}</Text>
</View>
</TouchableNativeFeedback>
);
render() {
return (
<FlatList
data={this.props.data}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
export default class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Notes',
headerStyle: { backgroundColor: 'rgb(255, 187, 0)' },
headerTitleStyle: { color: 'white' },
};
render() {
const { navigate } = this.props.navigation;
return (
<MyList
data={this.state.data}
load={this.state.load}
navig={this.props.navigation}
>
</MyList>
);
}
}
const Project = StackNavigator({
Home: { screen: HomeScreen },
NewNote: { screen: NewNoteScreen },
Note: { screen: NoteScreen }
});
AppRegistry.registerComponent('Project', () => Project);
Thanks for your help.
Because your MyList component is not part of your stack the navigation prop is not available for that.
You have 2 options.
First option you can pass the navigation prop manually to MyList
render() {
const { navigate } = this.props.navigation;
return (
<MyList
data={this.state.data}
load={this.state.load}
navigation={this.props.navigation}
>
</MyList>
);
}
Second option you can use withNavigation.
withNavigation is a Higher Order Component which passes the navigation
prop into a wrapped Component. It's useful when you cannot pass the
navigation prop into the component directly, or don't want to pass it
in case of a deeply nested child.
import { Button } 'react-native';
import { withNavigation } from 'react-navigation';
const MyComponent = ({ to, navigation }) => (
<Button title={`navigate to ${to}`} onPress={() => navigation.navigate(to)} />
);
const MyComponentWithNavigation = withNavigation(MyComponent);

Resources