Search code examples
javascriptreact-nativepicker

Clear state on custom component


Hey there I'm still learning with React Native and I have this custom picker component for iOS, and what I'm going to do is I want to clear the picker when I do onSubmit(). What should I do to set the picker state to its default state when I do onSubmit() from another component

PickerWrapper.js

class PickerWrapper extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            type_absen: this.props.items[0].description,
            modal: false
        }
    }

    render() {
        let picker;

        let iosPickerModal = (
            <Modal isVisible={this.state.modal} hideModalContentWhileAnimating={true} backdropColor={color.white} backdropOpacity={0.9} animationIn="zoomInDown" animationOut="zoomOutUp" animationInTiming={200} animationOutTiming={200} onBackButtonPress={() => this.setState({ modal: false })} onBackdropPress={() => this.setState({ modal: false })} >
                <View style={{ backgroundColor: color.white, width: 0.9 * windowWidth(), height: 0.3 * windowHeight(), justifyContent: 'center' }}>
                    <Picker
                        selectedValue={this.state.type_absen}
                        onValueChange={(itemValue, itemIndex) => {
                            this.setState({ type_absen: itemValue });
                            this.setState({ modal: false });
                            setTimeout(() => this.props.onSelect(itemValue), 1200);
                        }}
                    >
                        {this.props.items.map((item, key) => <Picker.Item label={item.description} value={item.id} key={item.id} />)}
                    </Picker>
                </View>
            </Modal>);

        if (Platform.OS === 'ios') {
            var idx = this.props.items.findIndex(item => item.id === this.state.type_absen);
            return (
                <View style={this.props.style}>
                    {iosPickerModal}
                    <TouchableOpacity onPress={() => this.setState({ modal: true })}>
                        <View style={{ flexDirection: 'row', height: this.props.height ? this.props.height : normalize(40), width: this.props.width ? this.props.width : 0.68 * windowWidth(), borderWidth: 1, borderColor: color.blue, alignItems: 'center', borderRadius: 5 }}>
                            <Text style={{ fontSize: fontSize.regular, marginRight: 30 }}> {idx !== -1 ? this.props.items[idx].description : this.state.type_absen}</Text>
                            <IconWrapper name='md-arrow-dropdown' type='ionicon' color={color.light_grey} size={20} onPress={() => this.setState({ modal: true })} style={{ position: 'absolute', right: 10 }} />
                        </View>
                    </TouchableOpacity>
                </View >);
        }
    }
   }

This is where I called my picker

App.js

class App extends Component {

  constructor() {
    super();
    this.state = {
      type_absen: ''
    }

    this.onSubmit = this.onSubmit.bind(this);
  }

onSubmit() {
  this.props.actionsAuth.changeSchedule(this.props.token, this.state.type_absen, (message) => alert(message));
  this.setState({ type_absen: ''}); 
 }
  
render(){
  let scheduleTypes = this.props.schedules;
  return(
      <PickerWrapper items={[{ "description": "Schedule Type", "id": "0" }, ...scheduleTypes]} onSelect={(item) => this.setState({ type_absen: item })} />
    );
  }
}


Solution

  • To update the child (PickerWrapper) from the parent, you have to create a new method in the child and call it from the parent.
    For this, the parent should have the child reference to have the ability to call some method from it

        class App extends Component {
    
          constructor() {
            super();
            this.state = {
              type_absen: ''
            }
    
            this.pickerRef = undefined;
            this.onSubmit = this.onSubmit.bind(this);
          }
    
        onSubmit() {
          this.props.actionsAuth.changeSchedule(this.props.token, this.state.type_absen, (message) => alert(message));
          this.setState({ type_absen: ''});
          this.pickerRef.reset();
         }
    
        render(){
          let scheduleTypes = this.props.schedules;
          return(
              <PickerWrapper
                  ref={ref => ref && this.pickerRef = ref}
                  items={[{ "description": "Schedule Type", "id": "0" }, ...scheduleTypes]}
                  onSelect={(item) => this.setState({ type_absen: item })} />
            );
          }
        }
    

    And then, create the reset method in the child :

    class PickerWrapper extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                type_absen: this.props.items[0].description,
                modal: false
            }
        }
    
        reset = () => {
            this.setState({type_absen: this.props.items[0].description, modal: false})
        }
    
        render() {
            let picker;
    
            let iosPickerModal = (
                <Modal isVisible={this.state.modal} hideModalContentWhileAnimating={true} backdropColor={color.white} backdropOpacity={0.9} animationIn="zoomInDown" animationOut="zoomOutUp" animationInTiming={200} animationOutTiming={200} onBackButtonPress={() => this.setState({ modal: false })} onBackdropPress={() => this.setState({ modal: false })} >
                    <View style={{ backgroundColor: color.white, width: 0.9 * windowWidth(), height: 0.3 * windowHeight(), justifyContent: 'center' }}>
                        <Picker
                            selectedValue={this.state.type_absen}
                            onValueChange={(itemValue, itemIndex) => {
                                this.setState({ type_absen: itemValue });
                                this.setState({ modal: false });
                                setTimeout(() => this.props.onSelect(itemValue), 1200);
                            }}
                        >
                            {this.props.items.map((item, key) => <Picker.Item label={item.description} value={item.id} key={item.id} />)}
                        </Picker>
                    </View>
                </Modal>);
    
            if (Platform.OS === 'ios') {
                var idx = this.props.items.findIndex(item => item.id === this.state.type_absen);
                return (
                    <View style={this.props.style}>
                        {iosPickerModal}
                        <TouchableOpacity onPress={() => this.setState({ modal: true })}>
                            <View style={{ flexDirection: 'row', height: this.props.height ? this.props.height : normalize(40), width: this.props.width ? this.props.width : 0.68 * windowWidth(), borderWidth: 1, borderColor: color.blue, alignItems: 'center', borderRadius: 5 }}>
                                <Text style={{ fontSize: fontSize.regular, marginRight: 30 }}> {idx !== -1 ? this.props.items[idx].description : this.state.type_absen}</Text>
                                <IconWrapper name='md-arrow-dropdown' type='ionicon' color={color.light_grey} size={20} onPress={() => this.setState({ modal: true })} style={{ position: 'absolute', right: 10 }} />
                            </View>
                        </TouchableOpacity>
                    </View >);
            }
        }
       }
    

    Additionnaly, your component is re-render each time a setState is set. You can avoid double re-render by switching

    this.setState({type_absen: itemValue});
    this.setState({modal: false});
    

    to this

    this.setState({type_absen: itemValue, modal: false});