leecade / leecade/react-native-swiper

Android only: The specified child already has a parent

Open
#772 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
10.5k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

Which OS ?

Android only.
iOS works fine.

Version

Which versions are you using:
"react": "16.0.0-alpha.12",
"react-native": "0.47.1",
"react-native-swiper": "^1.5.13",

Actual behaviour

Swiper renders the first view give to it but once I call this.refs.swiper.scrollBy(1) to programatically scroll to the next view, the app crashes with the error:

The specified child already has a parent. You must call removeView() on the child's parent first.

Same error as here.

It works fine in the iOS simulator, however when I run it on the android simulator or my device I encounter this error. I am adding my render method. Its a bit messy.

How to reproduce it>
render() {
    const {
      nameInput,
      textInput,
      questionText,
      nextTextStyle,
      metaDataStyle,
      inputContainer,
      upperContainer,
      arrowBackStyle,
      buttonContainer,
    } = styles;

    return (
      <View style={{ height: '100%', width: '100%', backgroundColor: 'rgb(220, 220, 220)' }}>
        <Header label='Añade tu banco' customCallBack={this.handleBackPress}/>

        <Swiper
          index={this.state.currentSlide}
          onIndexChanged={this.handleIndexChanged}
          ref={'swiper'}
          loop={false}
          scrollEnabled={false}
          dot={<View style={{backgroundColor: 'rgba(0,0,0,.0)'}} />}
          activeDot={<View style={{backgroundColor: 'rgba(0,0,0,.0)'}} />}
        >
          <View style={{ padding: 10, flex: 1 }}>
            <View style={{ flex: 1, ...shadow }}>
              <View style={upperContainer}>
                <Text style={questionText}>{'¿Eres individual o\nuna compañia / empresa?'}</Text>
              </View>

              <View style={[inputContainer, { flexDirection: 'row' }]}>
                <View style={{ width: '50%', height: '100%', ...centerItems }}>
                  <Text style={[questionText, { fontSize: width/22, marginBottom: 10 }]}>
                    Compañia
                  </Text>
                  <Checkbox
                    customStyles={{ width: 60, height: 40 }}
                    isChecked={this.state.type === "company" ? true: false}
                    onPress={() => this.setState({ type: "company" }, () => setTimeout(() => this.nextSlidePlease(), 200))}
                  />
                </View>
                <View style={{ width: '50%', height: '100%', ...centerItems }}>
                  <Text style={[questionText, { fontSize: width/22, marginBottom: 10 }]}>
                    Individual
                  </Text>
                  <Checkbox
                    customStyles={{ width: 60, height: 40 }}
                    isChecked={this.state.type === "individual" ? true: false}
                    onPress={() => this.setState({ type: "individual" }, () => setTimeout(() => this.nextSlidePlease(), 200))}
                  />
                </View>
              </View>
            </View>
          </View>

          <View style={{ padding: 10, flex: 1 }}>
            <View style={{ flex: 1, ...shadow, alignItems: 'center' }}>
              <View style={[upperContainer, { height: '18%' }]}>
                <Text style={questionText}>{'¿Cuál es tu\nfecha de nacimiento?'}</Text>
              </View>
              <View style={{ width: '80%', ...centerItems }}>
                <Text style={{ textAlign: 'center', color: 'rgb(70,70,70)' }}>Incluye un 0 adelante, si tu mes o día es un solo dígito: Día 5 → Día 05</Text>
              </View>
              <View style={[inputContainer, { flexDirection: 'row' }]}>
                <View style={{ width: '30%', height: '80%', ...centerItems }}>
                  <TextInput
                    ref={'dobday'}
                    style={[textInput, this.state.dob_day_focused ? { borderColor: 'rgb(0, 173, 237)', borderWidth: 3 } : {}]}
                    placeholder={'05'}
                    keyboardType={'numeric'}
                    numberOfLines={1}
                    maxLength={2}
                    underlineColorAndroid='transparent'
                    onFocus={() => this.handleDobFocus('day')}
                    value={this.state.dob_day}
                    onChangeText={text => this.handleDobInput(text, 'day')}
                  />
                <Text style={metaDataStyle}>Día</Text>
                </View>
                <View style={{ width: '30%', height: '80%', ...centerItems }}>
                  <TextInput
                    ref={'dobmonth'}
                    style={[textInput, this.state.dob_month_focused ? { borderColor: 'rgb(0, 173, 237)', borderWidth: 3 } : {}]}
                    placeholder={'01'}
                    keyboardType={'numeric'}
                    numberOfLines={1}
                    maxLength={2}
                    underlineColorAndroid='transparent'
                    onFocus={() => this.handleDobFocus('month')}
                    value={this.state.dob_month}
                    onChangeText={text => this.handleDobInput(text, 'month')}
                  />
                  <Text style={metaDataStyle}>Mes</Text>
                </View>
                <View style={{ width: '30%', height: '80%', ...centerItems }}>
                  <TextInput
                    ref={'dobyear'}
                    style={[textInput, this.state.dob_year_focused ? { borderColor: 'rgb(0, 173, 237)', borderWidth: 3 } : {}]}
                    placeholder={'1993'}
                    keyboardType={'numeric'}
                    numberOfLines={1}
                    maxLength={4}
                    underlineColorAndroid='transparent'
                    onFocus={() => this.handleDobFocus('year')}
                    value={this.state.dob_year}
                    onChangeText={text => this.handleDobInput(text, 'year')}
                  />
                  <Text style={metaDataStyle}>Año</Text>
                </View>
              </View>
            </View>
          </View>

          <View style={{ padding: 10, flex: 1 }}>
            <View style={{ flex: 1, ...shadow }}>
              <View style={upperContainer}>
                <Text style={questionText}>{'¿Cuál es tu\nnombre y apellido?'}</Text>
              </View>

              <View style={[inputContainer, { flexDirection: 'row', height: '15%', marginBottom: 20 }]}>
                <View style={{ width: '30%', height: '100%', alignItems: 'flex-start', paddingLeft: 15, justifyContent: 'space-around' }}>
                  <Text style={{ fontSize: width/22, fontWeight: '500', color: 'rgb(90,90,90)' }}>Nombre(s)</Text>
                  <Text style={{ fontSize: width/22, fontWeight: '500', color: 'rgb(90,90,90)' }}>Apellido(s)</Text>
                </View>
                <View style={{ width: '60%', height: '100%', alignItems: 'flex-start', paddingLeft: 15, justifyContent: 'space-around' }}>
                  <TextInput
                    value={this.state.first_name}
                    style={nameInput}
                    onChangeText={firstName => this.setState({ first_name: firstName })}
                    maxLength={20}
                    returnKeyType={'next'}
                    underlineColorAndroid='transparent'
                    onSubmitEditing={Keyboard.dismiss}
                  />

                  <TextInput
                    value={this.state.last_name}
                    style={nameInput}
                    onChangeText={lastName => this.setState({ last_name: lastName })}
                    maxLength={20}
                    returnKeyType={'next'}
                    underlineColorAndroid='transparent'
                    onSubmitEditing={Keyboard.dismiss}
                  />
                </View>
              </View>

              <View style={{ width: '100%', ...centerItems }}>
                <TouchableOpacity onPress={this.handleOnNamePress} style={{ width: '90%' }}>
                  <LinearGradient
                    colors={['#56CCF2', '#309ae8']}
                    style={buttonContainer}
                    start={{x: 0.0, y: 0.0}} end={{x: 1.2, y: 0.5}}
                    >
                    <Text style={nextTextStyle}>Siguiente</Text>
                  </LinearGradient>
                </TouchableOpacity>
              </View>

            </View>
          </View>

          {
            this.state.type === 'individual'
            ?
            <View style={{ padding: 10, flex: 1 }}>
              <View style={{ flex: 1, ...shadow }}>
                <View style={upperContainer}>
                  <Text style={questionText}>{'¿Cuál es tu DNI o NIE?'}</Text>
                </View>

                <View style={[inputContainer, { flexDirection: 'row', height: '7%', marginBottom: 20 }]}>
                  <View style={{ width: '35%', height: '100%', alignItems: 'flex-start', paddingLeft: 15, justifyContent: 'space-around' }}>
                    <Text style={{ fontSize: width/24, fontWeight: '500', color: 'rgb(90,90,90)' }}>DNI o NIE</Text>

                  </View>
                  <View style={{ width: '60%', height: '100%', alignItems: 'flex-start', paddingLeft: 15, justifyContent: 'space-around', borderWidth: 1 }}>
                    <TextInput
                      value={this.state.dni_nie}
                      onChangeText={nie => this.handleDniInput(nie)}
                      maxLength={9}
                      autoCapitalize={'characters'}
                      returnKeyType={'next'}
                      underlineColorAndroid='transparent'
                      onSubmitEditing={Keyboard.dismiss}
                      style={[nameInput,
                        this.state.dniIsValid === false
                        ?
                        { borderWidth: 1, borderColor: 'rgb(244, 66, 66)' }
                        :

                        this.state.dniIsValid === true
                        ?
                        { borderWidth: 1, borderColor: 'rgb(39, 209, 86)' }
                        :

                        { borderWidth: 1.5, borderColor: 'rgb(180,180,180)', }
                      ]}
                    />
                  </View>
                </View>

                <View style={{ width: '100%', ...centerItems }}>
                  <TouchableOpacity onPress={this.handleDniOrNieNextPress} style={{ width: '90%' }}>
                    <LinearGradient
                      colors={['#56CCF2', '#309ae8']}
                      style={buttonContainer}
                      start={{x: 0.0, y: 0.0}} end={{x: 1.2, y: 0.5}}
                      >
                      <Text style={nextTextStyle}>Siguiente</Text>
                    </LinearGradient>
                  </TouchableOpacity>
                </View>

              </View>
            </View>
            :

            <View style={{ padding: 10, flex: 1 }}>
              <View style={{ flex: 1, ...shadow }}>
                <View style={upperContainer}>
                  <Text style={[questionText, { fontSize: width/17, width: '97%' }]}>{'¿Cuál es el nombre (Razón social) y el NIF de la empresa?'}</Text>
                </View>
                <View style={[inputContainer, { flexDirection: 'row', height: '15%', marginBottom: 20 }]}>
                  <View style={{ width: '35%', height: '100%', alignItems: 'flex-start', paddingLeft: 15, justifyContent: 'space-around' }}>
                    <Text style={{ fontSize: width/24, fontWeight: '500', color: 'rgb(90,90,90)' }}>Nombre</Text>
                    <Text style={{ fontSize: width/22, fontWeight: '500', color: 'rgb(90,90,90)' }}>NIF</Text>
                  </View>
                  <View style={{ width: '60%', height: '100%', alignItems: 'flex-start', paddingLeft: 15, justifyContent: 'space-around' }}>
                    <TextInput
                      value={this.state.businessName}
                      style={nameInput}
                      onChangeText={input => this.setState({ businessName: input })}
                      maxLength={20}
                      returnKeyType={'next'}
                      underlineColorAndroid='transparent'
                      onSubmitEditing={Keyboard.dismiss}
                    />

                    <TextInput
                      value={this.state.nif}
                      onChangeText={nif => this.handleNifInput(nif)}
                      maxLength={9}
                      autoCapitalize={'characters'}
                      returnKeyType={'next'}
                      underlineColorAndroid='transparent'
                      onSubmitEditing={Keyboard.dismiss}
                      style={[nameInput,
                        this.state.nifIsValid === false
                        ?
                        { borderWidth: 1, borderColor: 'rgb(244, 66, 66)' }
                        :

                        this.state.nifIsValid === true
                        ?
                        { borderWidth: 1, borderColor: 'rgb(39, 209, 86)' }
                        :

                        {}
                      ]}
                    />
                  </View>
                </View>

                <View style={{ width: '100%', ...centerItems }}>
                  <TouchableOpacity onPress={this.handleBusinessDataInput} style={{ width: '90%' }}>
                    <LinearGradient
                      colors={['#56CCF2', '#309ae8']}
                      style={buttonContainer}
                      start={{x: 0.0, y: 0.0}} end={{x: 1.2, y: 0.5}}
                      >
                      <Text style={nextTextStyle}>Siguiente</Text>
                    </LinearGradient>
                  </TouchableOpacity>
                </View>

              </View>
            </View>
          }

          {
            this.state.type === 'individual'
            ?
            null

            :
            <View style={{ padding: 10, flex: 1 }}>
              <View style={{ flex: 1, ...shadow }}>
                <View style={upperContainer}>
                  <Text style={questionText}>{'¿Cúal es la dirección\nde la empresa?'}</Text>
                </View>

                <View style={{ width: '100%', ...centerItems }}>
                  <TouchableOpacity
                    onPress={() => Actions.googlePlaces({ isComingFromBankDetailsComponent: true })}
                    style={{ width: '90%' }}
                  >
                    <LinearGradient
                      colors={['#56CCF2', '#309ae8']}
                      style={buttonContainer}
                      start={{x: 0.0, y: 0.0}} end={{x: 1.2, y: 0.5}}
                      >
                      <Text style={nextTextStyle}>Buscar dirección</Text>
                    </LinearGradient>
                  </TouchableOpacity>
                </View>

              </View>
            </View>
          }

          <View style={{ padding: 10, flex: 1 }}>
            <View style={{ flex: 1, ...shadow }}>
              <View style={upperContainer}>
                <Text style={questionText}>{'Añade los datos de tu banco\ny empieza a recibir tu dinero.'}</Text>
              </View>

              <View style={{ width: '100%', height: 'auto', paddingLeft:20, paddingRight: 20 }}>
                <View style={{ width:'18%', height: 22, justifyContent: 'center' }}>
                  <Text style={{ fontWeight: '600', fontSize: width/22, color: 'rgb(70,70,70)' }}>IBAN</Text>
                </View>
                <View style={{ width: '100%', height: 45, ...centerItems }}>
                  <TextInput
                    maxLength={29}
                    autoCapitalize={'characters'}
                    numberOfLines={1}
                    keyboardType={'default'}
                    autoCorrect={false}
                    value={IBAN.printFormat(this.state.ibanInput)}
                    returnKeyType={'next'}
                    onChangeText={iban => this.verifyIban(iban)}
                    style={[{ borderWidth: 1, borderRadius: 2, height: '100%', width: '100%', textAlign: 'center', fontSize: width/24 },
                      this.state.ibanIsValid === false
                      ?
                      { borderWidth: 1, borderColor: 'rgb(244, 66, 66)' }
                      :

                      this.state.ibanIsValid === true
                      ?
                      { borderWidth: 1, borderColor: 'rgb(39, 209, 86)' }
                      :

                      {}
                    ]}
                  />
                </View>
              </View>

              <View style={{ width: '100%', height: 'auto', paddingLeft:20, paddingRight: 20, marginTop: 20 }}>
                <View style={{ width:'100%', height: 22, justifyContent: 'center' }}>
                  <Text style={{ fontWeight: '600', fontSize: width/22, color: 'rgb(70,70,70)' }}>TITULAR DE LA CUENTA</Text>
                </View>
                <View style={{ width: '100%', height: 45, ...centerItems }}>
                  <TextInput
                    ref={'owner'}
                    autoCapitalize={'words'}
                    maxLength={24}
                    numberOfLines={1}
                    keyboardType={'default'}
                    autoCorrect={false}
                    returnKeyType={'next'}
                    value={this.state.bankOwnerName}
                    onChangeText={owner => this.verifyBankOwnerName(owner)}
                    style={[{ borderWidth: 1, borderRadius: 2, height: '100%', width: '100%', textAlign: 'center', fontSize: width/24 },
                      this.state.bankOwnerNameIsValid === false
                      ?
                      { borderWidth: 1, borderColor: 'rgb(244, 66, 66)' }
                      :

                      this.state.bankOwnerNameIsValid === true
                      ?
                      { borderWidth: 1, borderColor: 'rgb(39, 209, 86)' }
                      :

                      {}
                    ]}
                  />
                </View>
              </View>

              <View style={{ height: 80 }}>
                {
                  this.state.ibanIsValid && this.state.bankOwnerNameIsValid
                  ?
                  <View style={{ width: '100%', height: 'auto', paddingLeft:20, paddingRight: 20, marginTop: 30, ...centerItems }}>
                    <TouchableOpacity onPress={this.createTokenFromBankAccount} style={{ width: '100%' }}>
                      <LinearGradient
                        colors={['#56CCF2', '#309ae8']}
                        style={buttonContainer}
                        start={{x: 0.0, y: 0.0}} end={{x: 1.2, y: 0.5}}
                        >
                        {
                          this.state.makingBankAccountRequest
                          ?
                          <ActivityIndicator color='white' />
                          :
                          <Text style={nextTextStyle}>¡Añadir!</Text>
                        }
                      </LinearGradient>
                    </TouchableOpacity>
                  </View>
                  :
                  null
                }
              </View>


              <View style={{ width: '100%', height: '20%', ...centerItems, marginTop: 0}}>
                <View style={{ width: '95%', height: '100%', ...centerItems  }}>
                  <Text style={{ fontSize: width/22, textAlign: 'center', color: 'rgb(90,90,90)' }}>
                    {'Para tu completa serguridad:\nLos datos de tu banco son\nmanejados por Stripe®.'}
                  </Text>
                </View>
              </View>
              <TouchableOpacity
                style={{ width: '100%', ...centerItems}}
                onPress={() => Linking.openURL('https://www.stripe.com').catch(err => console.error('An error occurred', err))}
              >
                <Image
                  resizeMode={'contain'}
                  style={{ height: 40 }}
                  source={require('../assets/stripeLogos/poweredByStripe.png')}
                />
              </TouchableOpacity>
            </View>
          </View>

          <View style={{ padding: 10, flex: 1 }}>
            <View style={{ flex: 1, ...shadow }}>
              <View style={[upperContainer, { height: '30%' }]}>
                <Text style={questionText}>¡Registramos tu banco!</Text>
                <Text style={[questionText, { fontWeight: '400', width: '98%', marginTop: 20 }]}>Al completar un trabajo, recibirás el dinero en un plazo máximo de 7 días.</Text>
              </View>

              <View style={{ width: '100%', height: 'auto', paddingLeft:20, paddingRight: 20, marginTop: 30, ...centerItems }}>
                <TouchableOpacity onPress={() => Actions.pop()} style={{ width: '100%' }}>
                  <LinearGradient
                    colors={['#56CCF2', '#309ae8']}
                    style={buttonContainer}
                    start={{x: 0.0, y: 0.0}} end={{x: 1.2, y: 0.5}}
                  >
                    <Text style={nextTextStyle}>Finalizar</Text>
                  </LinearGradient>
                </TouchableOpacity>
              </View>

            </View>
          </View>
        </Swiper>

        <Modal
          visible={this.state.debitCardInputVisible}
          animationType="fade"
          onRequestClose={() => this.setState({ debitCardInputVisible: false })}
          transparent={true}
        >
        <View style={{ height: '100%', backgroundColor: 'rgba(0, 0, 0, 0.9)', justifyContent: 'center' }}>
          <View style={{ height: '10%', flexDirection: 'row' }}>
            <TouchableOpacity
              onPress={() => this.setState({ debitCardInputVisible: false })}
              style={{ ...centerItems, width: '25%', height: '100%' }}
            >
              <Icon name="ios-close-circle-outline" style={arrowBackStyle}/>
            </TouchableOpacity>

            <View style={{ width: '50%' }}/>

            <TouchableOpacity
              style={{ ...centerItems, width: '25%', height: '100%' }}
              onPress={this.checkCardDataValidity}
            >
              <Icon name="ios-checkmark-circle-outline" style={arrowBackStyle}/>
            </TouchableOpacity>
          </View>

          <KeyboardAvoidingView
            style={{ height: '90%', justifyContent: 'center' }}
            behavior='padding'
            keyboardVerticalOffset={-20}
          >
            <CreditCardInput
              autoFocus
              cardScale={0.9}
              onChange={form => this.setState({ ...this.state, cardForm: { ...form } })}
              invalidColor={'rgb(234, 82, 65)'}
              inputStyle={{ color: 'white', borderWidth: 1, borderColor: 'rgba(0,0,0,.0)' }}
              labelStyle={{ color: 'rgb(250,250,250)', fontSize: width/28 }}
              containerStyle={{ borderWidth: 0 }}
              requiresName
              labels={{number: "NÚMERO TARJETA", expiry: "VENCE", cvc: "CVC/CCV", name: "NOMBRE"}}
            />
          </KeyboardAvoidingView>
        </View>

        </Modal>
      </View>
    );
  }

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the Swiper scrollBy call and the Android rendering path, using the supplied render method and React Native versions to reproduce the crash. Done means programmatic scrolling to the next view works on Android without the specified-child-parent error, while the existing iOS behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react-native
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.