Search code examples
reactjsreact-nativeandroid-emulatorfetch-api

React Native Posting/Fetching to Backend


I am having trouble posting data to an express backend with React Native via Android Emulator. The goal of the app is to allow customers to post microtask requests like home chores and lawn care where all the data will be stored to a AWS RDS MySQL Server. On the flip side there are freelancers who can view the customer posts through a feed. (For more background view here: https://askfavr.com) Now that you know the background onto the issue.

import React, { Component } from 'react';
import {
    StyleSheet, Text, View, Image,
    TextInput, KeyboardAvoidingView,
    TouchableOpacity, AsyncStorage, ScrollView
} from "react-native";


export default class App extends React.Component {
    state = {
        request: {
            C_Name: '', //this is a placeholder for the addRequest()  method
            Email: '',
            Phone: '',
            Category: '',
            Time_Length: '',
            Street: '',
            City: '',
            C_State: '',
            Zip: '',
            Finish_Before: '',
            Price: '',
            Details: ''
        }
    }

    /* Just like getRequests, addRequest does exactly what its named as well. It adds a request
      to the MySQL database following the initialization of the request state. The addRequest()
      uses the post method which then returns are query string containing the new data submitted */
    addRequest = _ => {
        const { request } = this.state;
        //query string set to remote server  
        fetch(`http://192.168.56.1:4000/requests/add?C_Name=${request.C_Name}&Email=${request.Email}&Phone=${request.Phone}&Category=${request.Category}&Time_Length=${request.Time_Length}&Street=${request.Street}&City=${request.City}&C_State=${request.C_State}&Zip=${request.Zip}&Finish_Before=${request.Finish_Before}&Price=${request.Price}&Details=${request.Details}`)
            .catch(err => console.error(err))
    }

    render() {
        const { request } = this.state; //set state for addRequest()
        return (
            <KeyboardAvoidingView behavior='padding' style={styles.wrapper}>
                <Text style={styles.header}> Yardwork Request </Text>
                <ScrollView>
                    <TextInput
                        style={styles.TextInput} placeholder='Name:' value={request.C_Name}
                        onChangeText={Name => this.setState(request.C_Name)}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Email:' value={request.Email}
                        onChangeText={e => this.setState({ request: { ...request, Email: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Phone:' value={request.Phone}
                        onChangeText={e => this.setState({ request: { ...request, Phone: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Category:' value={request.Category}
                        onChangeText={e => this.setState({ request: { ...request, Category: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Time of Job:' value={request.Time_Length}
                        onChangeText={e => this.setState({ request: { ...request, Time_Length: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Street:' value={request.Street}
                        onChangeText={e => this.setState({ request: { ...request, Street: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='City:' value={request.City}
                        onChangeText={e => this.setState({ request: { ...request, City: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='State:' value={request.C_State}
                        onChangeText={e => this.setState({ request: { ...request, C_State: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Zip:' value={request.Zip}
                        onChangeText={e => this.setState({ request: { ...request, Zip: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Complete By:' value={request.Finish_Before}
                        onChangeText={e => this.setState({ request: { ...request, Finish_Before: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Price:' value={request.Price}
                        onChangeText={e => this.setState({ request: { ...request, Price: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TextInput
                        style={styles.TextInput} placeholder='Details:' value={request.Details}
                        onChangeText={e => this.setState({ request: { ...request, Details: e.target.value } })}
                        underlineColorAndroid='black'
                    />
                    <TouchableOpacity
                        style={styles.btn}
                        onPress={this.addRequest}>
                        <Text> Request </Text>
                    </TouchableOpacity>
                </ScrollView>
            </KeyboardAvoidingView>
        );
    }
}

const styles = StyleSheet.create({
    wrapper: {
        flex: 1,
    },
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: '#FFF',
        paddingLeft: 40,
        paddingRight: 40,
    },
    header: {
        fontSize: 24,
        marginTop: 25,
        color: '#000000',
        fontWeight: 'bold',
    },
    TextInput: {
        alignSelf: 'stretch',
        padding: 16,
        marginBottom: 20,
        borderRadius: 12,
        color: '#000000',
        fontSize: 16,
        //backgroundColor: '#fff',
    },
    btn: {
        alignSelf: 'stretch',
        backgroundColor: 'green',
        padding: 20,
        borderRadius: 12,
        alignItems: 'center',
    },
    small: {
        fontSize: 15,
        color: '#000000',
        paddingTop: 10,
    },
});

when I try to run essentially the same thing in react native I get this error

setState(...) takes an object of state variables to update or a function which returns an object of state variables

but in Plain React I can use nearly the exact code with no problem and post data easily

import React, { Component } from 'react';
import './App.css';

class App extends Component {
    state = {
        requests: [], //null state for the getRequests() method
        request: {
            C_Name: '', //this is a placeholder for the addRequest()  method
            Email: '',
            Phone: '' ,
            Category: '',
            Time_Length:'' ,
            Street:'' ,
            City:'' , 
            C_State:'' ,
            Zip:'' , 
            Finish_Before:'' ,
            Price: '',
            Details: ''
        }
    }

    /* Mounts the "getRequests" to display on the screen, instead of writing the 
  full method within the componentDidMount() 
  componentDidMount() Purpose: this method takes place before rendering to
  essentially change the state of the arrays and variables if they are no
  longer null. */
    componentDidMount() {
        this.getRequests();
    }

    getRequests = _ => {
        fetch('http://192.168.56.1:4000/requests')
        .then( response => response.json())
        .then(response => this.setState({ requests: response.data}))
        .catch( err => console.error(err))
    }

    /* Just like getRequests, addRequest does exactly what its named as well. It adds a request
    to the MySQL database following the initialization of the request state. The addRequest()
    uses the post method which then returns are query string containing the new data submitted */
    addRequest =_ => {
        const { request } = this.state;
        //query string set to remote server  
        fetch(`http://localhost:4000/requests/add?C_Name=${request.C_Name}&Email=${request.Email}&Phone=${request.Phone}&Category=${request.Category}&Time_Length=${request.Time_Length}&Street=${request.Street}&City=${request.City}&C_State=${request.C_State}&Zip=${request.Zip}&Finish_Before=${request.Finish_Before}&Price=${request.Price}&Details=${request.Details}`) 
        .then(this.getRequests)
        .catch(err => console.error(err))
    }


    //Fields of Data for a FAVR Request: 
    //idyard_record, time_posted, C_Name, Email, Phone, Category, Time_Length, Street, City, C_State, Zip, Finish_Before, Price
    //Details, RequestID, Proposals
    renderRequest = ({ idyard_record, C_Name, Category }) => 
        <div key={idyard_record}>{C_Name}: <b>{Category} </b><br/><br/> </div> //displays all requests from the MySQL data similar to a flatlist
    render() {
        const { requests, request} = this.state; //set state for getRequests() && getRequest()


        const mainDiv = {
            align: 'center',
            justifyContent: 'center',
            flex: '1',
            margin: '2%'
        }
        const divStyle = {
            margin: '2%',
        };

        const inputBorder = {
            borderColor: 'lightgrey',
            padding: '5px',
            marginTop: '1%',
            marginLeft: '1%'
        }

        return (
            <div className="App" style={mainDiv} >
                {requests.map(this.renderRequest)}
                <div>
                    <div style={divStyle} > 
                        <label style={{align: 'left'}} > Name: </label> 
                        <input style={inputBorder} value={request.C_Name} 
                        onChange={e => this.setState({ request: {...request, C_Name: e.target.value}})} /> 
                    </div>
                    <div style={divStyle} >
                        <label> Email: </label> 
                        <input style={inputBorder} value={request.Email} 
                        onChange={e => this.setState({ request: {...request, Email: e.target.value}})} /> 
                    </div> 
                    <div style={divStyle} >
                        <label style={{textAlign: 'left'}} > Phone: </label> 
                        <input style={inputBorder} value={request.Phone} 
                        onChange={e => this.setState({ request: {...request, Phone: e.target.value}})} /> 
                    </div> 
                    <div style={divStyle} >
                        <label> Category: </label> 
                        <input style={inputBorder} value={request.Category} 
                        onChange={e => this.setState({ request: {...request, Category: e.target.value}})} /> 
                    </div> 
                    <div style={divStyle} >
                        <label> Time of Job: </label> 
                        <input style={inputBorder} value={request.Time_Length} 
                        onChange={e => this.setState({ request: {...request, Time_Length: e.target.value}})} /> 
                    </div> 
                    <div style={divStyle} >
                        <label> Street: </label> 
                        <input style={inputBorder} value={request.Street} 
                        onChange={e => this.setState({ request: {...request, Street: e.target.value}})} /> 
                    </div>
                    <div style={divStyle} >
                        <label> City: </label> 
                        <input style={inputBorder} value={request.City} 
                        onChange={e => this.setState({ request: {...request, City: e.target.value}})} /> 
                    </div>  
                    <div style={divStyle} >
                        <label> State: </label> 
                        <input style={inputBorder} value={request.C_State} 
                        onChange={e => this.setState({ request: {...request, C_State: e.target.value}})} /> 
                    </div>  
                    <div style={divStyle} >
                        <label> Zip: </label> 
                        <input style={inputBorder} value={request.Zip} placeholder='55901' 
                        onChange={e => this.setState({ request: {...request, Zip: e.target.value}})} /> 
                    </div> 
                    <div style={divStyle} >
                        <label> Complete By: </label> 
                        <input type="datetime-local" style={inputBorder} value={request.Finish_Before} placeholder='55901' 
                        onChange={e => this.setState({ request: {...request, Finish_Before: e.target.value}})} /> 
                    </div>
                    <div style={divStyle} >
                        <label> Price: </label> 
                        $<input type="number" style={inputBorder} value={request.Price} placeholder='$20'
                        onChange={e => this.setState({ request: { ...request, Price:  e.target.value }})} />
                    </div> 
                    <div style={divStyle} >
                        <label> Details: </label> 
                        <textarea rows={3} style={inputBorder} value={request.Details}
                        onChange={e => this.setState({ request: { ...request, Details:  e.target.value }})} />
                    </div> 
                    <button onClick={this.addRequest}> Request FAVR </button> 
                </div> 
            </div>
        );
    }
}

export default App;

Does anyone have suggestions to resolve this issue? I know the backend isn't the issue because the plain react app can post to it, and the emulator/plain react app can display the data from the MySQL DB with no issue.

image of react native error


Solution

  • The problematic one seems to be this one:

        onChangeText={Name => this.setState(request.C_Name)}
    

    This line is in the React Native code, but not the plain React code, so that's the difference between the two. You should be passing setState() an object, something like:

        this.setState({ request: {...request, C_Name: e.target.value}})
    

    similar to what you do for the other ones.