0

I have a view in which I am dynamically adding Input components from React Native Elements. Ultimately, I need to validate the text typed in Input, and so, for now, I am trying to change the errorMessage prop from false to true upon onChangeText. Unfortunately, nothing I try changes the errorMessage even though the state component changes to true. I have a Snack, and, for convenience, my code follows:

import * as React from 'react'
import { ScrollView, StyleSheet, View } from 'react-native'
import { Button, Icon, Input, Text } from 'react-native-elements'
import * as Shortid from 'shortid'

export default class App extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      timeInputs: [],
      validities: []
    }

    this.addTimeInput = this.addTimeInput.bind(this)
    this.removeTimeInput = this.removeTimeInput.bind(this)
    this.mutateValidities = this.mutateValidities.bind(this)
  }

  componentDidMount () {
    this.addTimeInput()
  }

  addTimeInput () {
    const identifier = Shortid.generate()
    const timeInputs = this.state.timeInputs
    const validities = this.state.validities

    let isTimeValid = false
    let time = ''

    new Promise(
      (resolve, reject) => {
        resolve(
          validities.push(
            { id: identifier, value: isTimeValid }
          ),
          timeInputs.push(
            <View key = { identifier }>
              <View style = { styles.row }>
                <Input
                  errorMessage = {
                    this.state.validities.filter(
                      validity => validity.id !== identifier
                    ).value == true
                      ? 'Valid'
                      : 'Invalid'
                  }
                  errorStyle = { styles.error }
                  onChangeText = {
                    value => {
                      time = value
                      this.mutateValidities(identifier, true)
                      console.log('TIME ' + identifier + ': ' + time)
                    }
                  }
                  placeholder = 'HH:MM AM/PM'
                  ref = { React.createRef() }
                />
                <Icon
                  color = { colors.dark }
                  name = 'add-circle'
                  onPress = { () => this.addTimeInput() }
                  type = 'material'
                />
                <Icon
                  color = { colors.dark }
                  name = 'remove-circle'
                  onPress = {
                    () => {
                      if (this.state.timeInputs.length > 1) {
                        this.removeTimeInput(identifier)
                      } else {
                        console.log('LENGTH: ' + this.state.timeInputs.length)
                      }
                    }
                  }
                  type = 'material'
                />
              </View>
            </View>
          )
        )
      }
    )
      .then(
        this.setState(
          {
            timeInputs: timeInputs,
            validities: validities
          }
        )
      )
      .catch(
        (reason) => {
          console.log(
            'Failed to create time-input because of the following: ' + reason
          )
        }
      )
  }

  mutateValidities (key, value) {
    this.setState(
      {
        validities: this.state.validities.map(
          validity => validity.id === key
            ? {...validity, value: value}
            : validity
        )
      }
    )
  }

  removeTimeInput (key) {
    this.setState(
      {
        timeInputs: this.state.timeInputs.filter(
          timeInput => timeInput.key !== key
        ),
        validities: this.state.validities.filter(
          validity => validity.id !== key
        )
      }
    )
  }

  render () {
    return (
      <ScrollView contentContainerStyle = { styles.container }>
        <Text h4 style = { styles.title }>Time Inputs</Text>
        {
          this.state.timeInputs.map(
            (value) => { return value }
          )
        }
        <Button
          buttonStyle = { styles.button }
          onPress = {
            () => this.state.validities.map(
              validity => console.log(validity.id + validity.value)
            )
          }
          title = 'Log Validities'
        />
      </ScrollView>
    )
  }
}

const colors = {
  dark: 'steelblue',
  light: 'aliceblue',
  medium: 'lightsteelblue',
  error: 'firebrick'
}

const styles = StyleSheet.create(
  {
    button: {
      backgroundColor: colors.dark,
      margin: 5
    },
    container: {
      alignItems: 'center',
      backgroundColor: colors.light,
      flex: 1,
      justifyContent: 'center'
    },
    error: {
      color: colors.error,
      fontSize: 12,
      margin: 5
    },
    row: {
      alignItems: 'center',
      flexDirection: 'row',
      margin: 5,
      width: '80%'
    },
    title: {
      margin: 5
    }
  }
)

Due credit goes to McGregor (2017) for getting me this far, but I am still stuck.

Reference:

McGregor, L. (2017, October 2) Whats the best way to update an object in an array in ReactJS? [Stack Overflow answer]. Retrieved from https://stackoverflow.com/a/46518653/6084947

michaelgill1969
  • 131
  • 2
  • 9
  • I'm getting closer. I have taken the view from addTimeInput and put it in its own render function. I am NOT passing the arrays as props. Rather, I am passing (what I think are) pure functions that change the arrays in state back in the App component. The only constant I am passing is the shortid identifier, which is the only parameter I am using for the functions. Everything works except the validation, which I think I can get done early next week. [Hellsing (2014)](https://stackoverflow.com/a/26254086/6084947) has been useful in getting this through my thick skull. Answer coming soon. – michaelgill1969 Sep 20 '19 at 00:58
  • Finally got it working. I'll write up the solution tomorrow or Wednesday. – michaelgill1969 Sep 23 '19 at 22:28

1 Answers1

0

I believe my problem was that I was trying to trigger changes in the view that were too deeply nested. Even assuming the props were passed correctly, the view would not have updated when the state changed because React only compares props and state so far. Following the recommendation made in "The Power Of Not Mutating Data" (Facebook, 2019), I avoided these issues with shallow comparison by making the text-input a full React component. I also applied React Redux to manage the state in a central store instead of passing props back and forth, which I never could get to work even after abstracting the text-input.

The code is now spread out between too many different files to post here, but anyone interested can view it on GitHub or Snack.

Reference:

Facebook. (2019). Optimizing Performance [Documentation]. Retrieved from https://reactjs.org/docs/optimizing-performance.html#the-power-of-not-mutating-data

michaelgill1969
  • 131
  • 2
  • 9