0
votes

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.

2
Use this getDerivedStateFromProps life cycle method - Rajendran Nadar
did my solution work?? - Rajendran Nadar
@RaajNadar I had the same issue after using your suggested lifecycle method. - Danial
I have fixed this issue. Actually my concepts were not right. I think props are received only in render method. but not in lifecycle method. because when i call a method from render then it has all the props but when i get props in lifecycle method it does not have all props at the same time. - Danial
Default props should fix the issue, because it will be loaded before the render method. - Rajendran Nadar

2 Answers

1
votes

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

0
votes

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