Ken Pham
Ken Pham

Reputation: 295

How to change props of TextInput component in a FlatList?

I'm a newbie in React Native.

What I'm trying to do is making a Google Maps-like app. At the MainMap.js screen, when we enter,the screen will immediately generate 2 search bars. The first one will have the text "Your location". The second one and so on will be empty for users to type in for searching location.

But, I'm having some problems with the FlatList component. In my PlaceInput component, I use the defaultValue, as a prop, for the text input. And then in the MainMap.js, I will have a state which initially be set as "Your Location", then I'll change it to null when the FlatList starts rendering from the 2nd PlaceInput component.

Here's the MainMap.js*

import React from 'react';
import { 
    TouchableWithoutFeedback,
    StyleSheet,
    Keyboard,
    PermissionsAndroid,
    Platform, 
    View,
    Button,
    FlatList,
    Dimensions
} from 'react-native';

import PlaceInput from '../components/PlaceInput';

import axios from 'axios';
import PolyLine from '@mapbox/polyline';
import MapView, {Polyline, Marker} from 'react-native-maps';
import Geolocation from 'react-native-geolocation-service';

const INCREMENT = 1;
const HEIGHT = Dimensions.get('window').height;
const WIDTH = Dimensions.get('window').width;

class MainMap extends React.Component{

    constructor(props){
        super(props);

        this.state={

            _userLocationDisplayed: null,
            userLatitude: 0,
            userLongitude: 0,

            numOfInput:[0,1],
            counter: 1,
        };
    };
    componentDidMount(){
        this._requestUserLocation();
    };

    // Get user current location

    // Ask user permission for current location
    // Request the Directions API from Google
    // Get the formatted_address & name from Google Places API
    // Adding a search bar
    onAddSearch(){
        this.setState((state) => ({
            counter: state.counter + INCREMENT,
            numOfInput: [...state.numOfInput, state.counter],
        }));
    };

    onChangeSearchDisplay(){
        this.setState({
            _userLocationDisplayed: null
        })
    };

    render(){

            return(
                <TouchableWithoutFeedback onPress={this.hideKeyboard} >
                    <View style={styles.container} >
                        <View style={{height: HEIGHT/2.5 }}>
                            <FlatList
                                data={this.state.numOfInput}
                                keyExtractor={(item, index) => item}
                                renderItem={itemData => {
                                    return(
                                        <PlaceInput
                                            id={itemData.item}
                                            onDelete={this.onDeleteSearch}
                                            showDirectionOnMap={this.showDirectionOnMap}
                                            userLatitude={userLatitude}
                                            userLongitude={userLongitude}
                                            userLocationDisplayed={this.state._userLocationDisplayed}
                                        />
                                    )
                                }}
                            />
                        </View>
                    </View>
                </TouchableWithoutFeedback>
            )
        }

    }

//}


export default MainMap;

const styles = StyleSheet.create({
    container:{
        flex: 1
    },
    map:{
        ...StyleSheet.absoluteFillObject
    },
});


Here's the PlaceInput component

import React from 'react';
import {
    View,
    TextInput,
    StyleSheet,
    Text,
    Dimensions,
    TouchableOpacity,
    Keyboard,
} from 'react-native';
import axios from 'axios';
import _ from 'lodash'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'

const WIDTH = Dimensions.get('window').width;
const HEIGHT = Dimensions.get('window').height;

class PlaceInput extends React.Component{

    constructor(props){
        super(props);
        this.state={
            ...
        }
        ...
    }

    render() {
        // console.log(this.state);
        // Code for displaying the suggestions from the Google Place API
        // Don't care about it too much :)))
        const predictions = this.state.predictions.map(prediction => {
            const { id, structured_formatting, place_id } = prediction;
            return(
                <TouchableOpacity 
                    key={id} 
                    onPress={() => this.setDestination(structured_formatting.main_text, place_id)}    
                >
                    <View style={styles.suggestion}>
                        <Text style={styles.mainText}>{structured_formatting.main_text}</Text>
                        <Text style={styles.secText}>{structured_formatting.secondary_text}</Text>
                    </View>
                </TouchableOpacity>
            );
        } )

        return (
            <View style={{flex: 1, flexDirection: 'column'}} key={this.props.id}>
                <View style={styles.buttonContainer}>
                    <View style={{flex: 1, alignItems: 'center'}}>
                            <Text style={{fontSize: 8}}>{'\u25A0'}</Text>
                    </View>
                    <View style={{flex: 4}}>
                        <TextInput 
                            key={this.id}
                            autoCorrect={false}
                            autoCapitalize='none'
                            style={styles.inputStyle}
                            placeholder='Search your places'
                            onChangeText={(input) => {
                                this.setState({destinationInput: input});
                                this.getPlacesDebounced(input);
                            }}
                            value={this.state.destinationInput}

                            {/*What I'm trying here as mentioned*/}
                            defaultValue={this.props.userLocationDisplayed}

                        />
                    </View>
                    <View style={styles.rightCol}>
                            <TouchableOpacity onPress={() => this.props.onDelete(this.props.id)}>
                                <Icon name='delete' size={25} style={{alignSelf: 'center'}} />
                            </TouchableOpacity>
                    </View>

                </View>
                {predictions}
            </View>
        )
    } 
}

const styles = StyleSheet.create({
    buttonContainer:{
        flexDirection: 'row',
        height: (HEIGHT - 690),
        width: (WIDTH-48),
        marginTop: 55,
        padding: 5,
        backgroundColor: 'white',
        shadowColor: '#000000',
        elevation: 7,
        shadowRadius: 5,
        shadowOpacity: 1,
        borderRadius: 5,
        alignItems: 'center',
        alignSelf:'center'
    },
    inputStyle:{
        fontFamily: 'sans-serif-thin', 
        fontSize: 16, 
        color: 'black',
        fontWeight: 'bold'
    },
    suggestion:{
        backgroundColor: 'white',
        padding: 10,
        borderWidth: 0.5,
        width: (WIDTH-48),
        alignSelf: 'center'
    },
    secText:{
        color: '#777'
    },
    mainText:{
        color: '#000'
    },
    rightCol:{
        flex: 1,
        borderLeftWidth: 1,
        borderColor: '#ededed',
    },
})

export default PlaceInput;

I'd love to hear your comments for helping me.

Also, feel free to give out other ways too since I think my way isn't optimized enough. And I'm building this for production too.

Upvotes: 0

Views: 424

Answers (2)

Ken Pham
Ken Pham

Reputation: 295

I take advantage of Drew Reese's answer but It doesn't work

I found out why it doesn't work because of the value prop, whose value is set by this.state.destinationInput which is " " in the state in the constructor. I again use Drew's way in the value prop instead, and it works

                     <TextInput 
                        key={this.id}
                        autoCorrect={false}
                        autoCapitalize='none'
                        style={styles.inputStyle}
                        placeholder='Search your places'
                        onChangeText={(input) => {
                            this.setState({destinationInput: input});
                            this.getPlacesDebounced(input);
                        }}
                        value={this.props.displayDefaultValue ? this.props.defaultValue : this.state.destinationInput}
                    />

BIG thanks to Drew Reese

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202916

If I understand your question correctly, you're asking how to conditionally set a prop value based upon where it is in the flatlist data. Basically you want the first PlaceInput component to have a displayed "entered" text value of "Your Location" and the rest to have nothing.

Update API of PlaceInput to take in another prop to indicate displaying a default value or not.

PlaceInput.js

...
<TextInput 
  key={this.id}
  autoCorrect={false}
  autoCapitalize='none'
  style={styles.inputStyle}
  placeholder='Search your places'
  onChangeText={(input) => {
    this.setState({destinationInput: input});
    this.getPlacesDebounced(input);
  }}
  value={this.state.destinationInput}
  defaultValue={this.props.displayDefaultValue ? this.props.defaultValue : null}
/>
...

And pass in whether or not any specific PlaceInput should display it or not. Since you want only the first to display and the rest to not, using the array index is a good place to start. Here we can leverage the fact that in javascript 0 is a falsey value, while all other numbers are truthy. Using !index then !0 is true while !1, !2, etc, are all false.

MainMap.js

<FlatList
  data={this.state.numOfInput}
  keyExtractor={(item, index) => item}
  renderItem={({ index, item }) => {
    return(
      <PlaceInput
        id={item}
        defaultValue="Your Location"
        displayDefaultValue={!index} // index 0 is falsey, all others truthy
        onDelete={this.onDeleteSearch}
        showDirectionOnMap={this.showDirectionOnMap}
        userLatitude={userLatitude}
        userLongitude={userLongitude}
        userLocationDisplayed={this.state._userLocationDisplayed}
      />
    )
  }}
/>

Upvotes: 1

Related Questions