React Native FlatList未加载MySQL查询结果问题排查与优化咨询
Let's break down the issues in your code and fix them step by step, plus share some cleaner, more efficient approaches.
First, the root cause of your FlatList not rendering data
Looking at your PHP code, when there are no results, you return a plain string 'No Results Found.' instead of a consistent JSON object with an item field. When your frontend tries to access responseJson.item on a string, it gets undefined, setting dataPeople to undefined—which makes FlatList render nothing. Even when you do get valid results, there's a redundant loop in your PHP response building that could cause unexpected behavior.
Fixes for the PHP code
- Prevent SQL Injection: Never directly interpolate user input into SQL queries. Use prepared statements instead to keep your database safe.
- Consistent Response Format: Always return a JSON object with the
itemarray, even when there are no results. This way your frontend doesn't have to handle mixed response types. - Simplify Result Fetching: Use
fetch_all()to get all results at once instead of looping through each row.
Here's the corrected PHP code:
<?php include 'koneksi-lkl.php'; $json = file_get_contents('php://input'); $obj = json_decode($json,true); $itemcode = $obj['itemcode'] ?? ''; // Handle case where itemcode is not provided // Use prepared statement to avoid SQL injection $Sql_Query = "SELECT id, itemcode, description FROM article WHERE itemcode = ? LIMIT 1"; $stmt = $con->prepare($Sql_Query); $stmt->bind_param("s", $itemcode); // "s" denotes string type $stmt->execute(); $result = $stmt->get_result(); // Return consistent structure: always include item array $response = ["item" => []]; if ($result->num_rows > 0) { $response["item"] = $result->fetch_all(MYSQLI_ASSOC); } echo json_encode($response); $stmt->close(); $con->close(); ?>
Fixes for the React Native code
Even with the fixed PHP, you should add proper loading/empty state handling to give users clear feedback. Also, we'll fix edge cases where the response might not have an item field:
// Updated SearchArticle.js (class component version) constructor(props) { super(props); this.state = { dataPeople: [], isLoading: false, TextInput_ItemCode: "" }; } _fetchItem = () => { this.setState({ isLoading: true }); // Show loading state fetch("http://192.168.181.59/article/SearchArticleList.php", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ itemcode: this.state.TextInput_ItemCode, }) }) .then((response) => response.json()) .then((responseJson)=>{ console.log(responseJson); this.setState({ isLoading: false, dataPeople: responseJson.item || [] // Fallback to empty array if needed }); }) .catch((error)=>{ console.error(error); this.setState({ isLoading: false, dataPeople: [] }); Alert.alert("Error", "Failed to fetch data. Please check your network."); }) }; // Update render to handle loading/empty states render() { const { dataPeople, isLoading } = this.state; return ( <View style={styles.MainContainer}> <Text style={{ fontSize: 20, textAlign: "center", marginBottom: 7 }}> Search Article </Text> <TextInput placeholder="Itemcode" onChangeText={(TextInputValue) => this.setState({ TextInput_ItemCode: TextInputValue })} underlineColorAndroid="transparent" keyboardType='numeric' style={styles.TextInputStyleClass} /> <TouchableOpacity activeOpacity={0.4} style={styles.TouchableOpacityStyle} onPress={this._fetchItem} disabled={isLoading} // Prevent duplicate requests > <Text style={styles.TextStyle}> {isLoading ? "SEARCHING..." : "SEARCH"} </Text> </TouchableOpacity> {/* Handle loading, empty, and data states */} {isLoading ? ( <ActivityIndicator size="large" color="#0000ff" style={{ marginTop: 20 }} /> ) : dataPeople.length === 0 ? ( <Text style={{ textAlign: "center", marginTop: 20, color: "gray" }}> No results found. Try a different item code. </Text> ) : ( <FlatList data={dataPeople} renderItem={this._itemCoponent} keyExtractor={(item) => item.id} // Use unique item ID instead of index ItemSeparatorComponent={this._separatorComponent} /> )} </View> ); }
More Efficient & Clean Implementation (Functional Component with Hooks)
Class components work, but functional components with React Hooks are more modern and concise. Here's a polished version:
import React, { useState } from 'react'; import { View, Text, TextInput, TouchableOpacity, FlatList, ActivityIndicator, Alert, StyleSheet } from 'react-native'; const SearchArticle = () => { const [dataPeople, setDataPeople] = useState([]); const [isLoading, setIsLoading] = useState(false); const [itemCode, setItemCode] = useState(""); const fetchItem = async () => { // Validate input before making request if (!itemCode.trim()) { Alert.alert("Warning", "Please enter an item code."); return; } setIsLoading(true); try { const response = await fetch("http://192.168.181.59/article/SearchArticleList.php", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ itemcode: itemCode }) }); const responseJson = await response.json(); setDataPeople(responseJson.item || []); } catch (error) { console.error(error); Alert.alert("Error", "Failed to fetch data. Please check your network."); setDataPeople([]); } finally { setIsLoading(false); } }; const renderItem = ({ item }) => ( <View style={styles.itemContainer}> <View style={styles.itemContent}> <Text style={styles.itemCodeText}>{item.id} - {item.itemcode}</Text> <Text style={styles.itemDescText}>{item.description}</Text> </View> </View> ); const renderSeparator = () => ( <View style={styles.separator} /> ); return ( <View style={styles.container}> <Text style={styles.title}>Search Article</Text> <TextInput placeholder="Itemcode" value={itemCode} onChangeText={setItemCode} keyboardType="numeric" style={styles.input} /> <TouchableOpacity style={styles.searchButton} onPress={fetchItem} disabled={isLoading} > <Text style={styles.buttonText}>{isLoading ? "SEARCHING..." : "SEARCH"}</Text> </TouchableOpacity> {isLoading ? ( <ActivityIndicator size="large" color="#2196F3" style={styles.loader} /> ) : dataPeople.length === 0 ? ( <Text style={styles.emptyText}>No results found. Try another item code.</Text> ) : ( <FlatList data={dataPeople} renderItem={renderItem} keyExtractor={(item) => item.id} ItemSeparatorComponent={renderSeparator} style={styles.list} /> )} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 16, backgroundColor: "#fff" }, title: { fontSize: 20, textAlign: "center", marginBottom: 16 }, input: { height: 40, borderColor: "#ccc", borderWidth: 1, borderRadius: 4, paddingHorizontal: 8, marginBottom: 16 }, searchButton: { backgroundColor: "#2196F3", padding: 12, borderRadius: 4, alignItems: "center" }, buttonText: { color: "#fff", fontSize: 16 }, loader: { marginTop: 24 }, emptyText: { textAlign: "center", marginTop: 24, color: "#666", fontSize: 16 }, list: { marginTop: 16 }, itemContainer: { flex: 1, marginLeft: 8 }, itemContent: { flex: 2, paddingVertical: 8 }, itemCodeText: { fontSize: 14, fontWeight: "500" }, itemDescText: { fontSize: 12, color: "#666", marginTop: 4 }, separator: { height: 0.5, backgroundColor: "#ccc", marginVertical: 8 } }); export default SearchArticle;
Key Improvements
- SQL Injection Protection: Prepared statements in PHP eliminate a critical security risk.
- Consistent API Responses: Your frontend always gets a predictable JSON structure, avoiding runtime errors.
- User Feedback: Loading states, empty state messages, and input validation keep users informed.
- Modern React Syntax: Functional components with Hooks are easier to read, test, and maintain.
- Error Handling: Clear alerts for network errors help users troubleshoot issues.
内容的提问来源于stack exchange,提问作者kank95

