我是React Native的移动开发新手。我正在使用Expo。我无法管理状态。
我有一个SearchTextBox
组件,我想从父组件中设置 text 属性,但是我无法使用useState
对其进行管理。
import React, { useState, useEffect } from "react";
import { TouchableOpacity } from "react-native-gesture-handler";
import { Ionicons } from "@expo/vector-icons";
import { StyleSheet, TextInput, View } from "react-native";
import defaultStyles from "../config/styles";
function SearchTextBox({
searchQuery,
width = "100%",
onSearch,
...otherProps
}) {
const [query, setQuery] = useState(searchQuery);
const [isClearable, setIsClearable] = useState(
query != null && query.length > 0
);
const onChange = (e) => {
const { text } = e.nativeEvent;
setQuery(text);
const length = text.length;
setIsClearable(length > 0);
if (length >= 2 && onSearch) onSearch(text);
};
const onClear = () => {
setQuery("");
setIsClearable(false);
};
return (
<View style={styles.container}>
<View
style={{
width: 72,
}}
>
<Ionicons
name="md-search"
size={32}
color={defaultStyles.colors.medium}
style={styles.icon}
/>
</View>
<View
style={{
flex: 2,
}}
>
<TextInput
autoCorrect={false}
autoFocus={false}
autoCapitalize="none"
placeholder="Search"
placeholderTextColor={defaultStyles.colors.medium}
style={[defaultStyles.text, styles.text]}
onChange={onChange}
value={query}
{...otherProps}
/>
</View>
{isClearable && (
<View>
<TouchableOpacity onPress={onClear}>
<Ionicons
name="md-close"
size={32}
color={defaultStyles.colors.medium}
style={styles.icon}
/>
</TouchableOpacity>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: defaultStyles.colors.light,
borderRadius: 24,
flexDirection: "row",
flex: 1,
marginVertical: 10,
},
icon: {
margin: 15,
paddingRight: 15,
},
text: {
flexGrow: 1,
fontSize: 18,
},
});
export default SearchTextBox;
我这样使用:
import React, { useState, useEffect, useRef } from "react";
import { View, StyleSheet, FlatList, AsyncStorage } from "react-native";
import { AdMobBanner } from "expo-ads-admob";
import * as Device from "expo-device";
import { ListItem, ListItemSeparator } from "../components/lists/";
import Screen from "../components/Screen";
import SearchTextBox from "../components/SearchTextBox";
import AnimatedTextList from "../components/AnimatedTextList";
import colors from "../config/colors";
import searchApi from "../api/search";
import useApi from "../hooks/useApi";
import ads from "../config/ads";
function HomeScreen() {
const search = useApi(searchApi.search);
const [data, setData] = useState([]);
const [showRandomWords, setShowRandomWords] = useState(true);
const [randomWords, setRandomWords] = useState([]);
const [bannerType, setBannerType] = useState("banner");
const [initialQuery, setInitialQuery] = useState();
const getDeviceTypeAsync = async () => {
const deviceType = await Device.getDeviceTypeAsync();
setBannerType(
deviceType === Device.DeviceType.TABLET ? "fullBanner" : "banner"
);
};
useEffect(() => {
getDeviceTypeAsync;
}, []);
const onSearch = async (value) => {
setShowRandomWords(value == null || value == undefined || value === "");
const result = await search.request(value);
if (!result.ok) return;
setData(result.data.data);
};
useEffect(() => {
const getRandomWords = async () => {
try {
const result = await AsyncStorage.getItem("random_words");
if (!result) return;
setRandomWords(JSON.parse(result));
setShowRandomWords(true);
} catch (error) {
console.log(error);
}
};
getRandomWords();
}, []);
const bannerError = (error) => {
console.log("An error occured while trying to show AdMob", error);
};
const onRandomWordPress = (item) => {
setInitialQuery(item.title);
onSearch(initialQuery);
};
return (
<Screen>
<View style={styles.searchBox}>
<SearchTextBox
searchQuery={initialQuery}
width="90%"
onSearch={onSearch}
/>
</View>
{showRandomWords && (
<View>
<AnimatedTextList items={randomWords} onPress={onRandomWordPress} />
</View>
)}
<View style={styles.result}>
<FlatList
data={data}
keyExtractor={(word) => word.id.toString()}
renderItem={({ item }) => (
<ListItem title={item.word} subTitle={item.descp} />
)}
ItemSeparatorComponent={ListItemSeparator}
/>
</View>
<View style={styles.bottomBanner}>
<AdMobBanner
style={styles.bottomBanner}
bannerSize={bannerType}
adUnitID={ads.unitID}
servePersonalizedAds
didFailToReceiveAdWithError={bannerError}
/>
</View>
</Screen>
);
}
const styles = StyleSheet.create({
bottomBanner: {
alignContent: "center",
alignItems: "center",
alignSelf: "center",
bottom: 0,
position: "absolute",
width: "100%",
},
container: {
flex: 1,
alignItems: "center",
width: "100%",
flexDirection: "column",
},
searchBox: {
backgroundColor: colors.white,
height: 100,
width: "100%",
padding: 10,
},
result: {
width: "100%",
flex: 1,
},
});
export default HomeScreen;
这是我绑定的财产
<SearchTextBox
searchQuery={initialQuery}
width="90%"
onSearch={onSearch}
/>
在onRandomWordPress
中,我设置了变量,但是它不会更新输入的文本。
const onRandomWordPress = (item) => {
setInitialQuery(item.title);
onSearch(initialQuery);
};
执行此操作的正确方法是什么,或者我的实施有什么问题?
谢谢!
答案 0 :(得分:0)
您不应该将此处的random word
传递给您的onSearch
函数。
const onRandomWordPress = (item) => {
setInitialQuery(item.title);
onSearch(item.title);
};
答案 1 :(得分:0)
这就是我的成就。我将查询绑定(始终)绑定到父组件,几乎所有内容都由父管理。
现在,我有了一个更清洁的SearchTextBox
组件。
function SearchTextBox({
searchQuery,
onTextChange,
onQueryClear,
width = "100%",
...otherProps
}) {
const hasSearchQuery = () => {
return searchQuery != null && searchQuery.length > 0;
};
const onChange = (e) => {
const { text } = e.nativeEvent;
onTextChange(text);
};
return (
<View style={styles.container}>
<View
style={{
width: 72,
}}
>
<Ionicons
name="md-search"
size={32}
color={defaultStyles.colors.medium}
style={styles.icon}
/>
</View>
<View
style={{
flex: 2,
}}
>
<TextInput
autoCorrect={false}
autoFocus={false}
autoCapitalize="none"
placeholderTextColor={defaultStyles.colors.medium}
style={[defaultStyles.text, styles.text]}
onChange={onChange}
value={searchQuery}
{...otherProps}
/>
</View>
{hasSearchQuery() && (
<View>
<TouchableOpacity onPress={onQueryClear}>
<Ionicons
name="md-close"
size={32}
color={defaultStyles.colors.medium}
style={styles.icon}
/>
</TouchableOpacity>
</View>
)}
</View>
);
}
并管理父级的部分:
function HomeScreen() {
const search = useApi(searchApi.search);
const [data, setData] = useState([]);
const [showRandomWords, setShowRandomWords] = useState(true);
const [randomWords, setRandomWords] = useState([]);
const [initialQuery, setInitialQuery] = useState();
const onSearch = async (value) => {
const isEmptyQuery = value == null || value == undefined || value === "";
setShowRandomWords(isEmptyQuery);
const result = await search.request(value);
if (!result.ok) return;
setData(result.data.data);
};
const onQueryClear = () => {
setInitialQuery("");
setData([]);
setShowRandomWords(true);
};
const onTextChange = (text) => {
setInitialQuery(text);
if (text.length > 2) onSearch(text);
};
const onRandomWordPress = (item) => {
setInitialQuery(item.title);
onSearch(item.title);
};
useEffect(() => {
getRandomWords();
}, []);
return (
<Screen>
<View style={styles.searchBox}>
<SearchTextBox
searchQuery={initialQuery}
onTextChange={onTextChange}
onQueryClear={onQueryClear}
width="90%"
/>
</View>
{showRandomWords && (
<View>
<AnimatedTextList items={randomWords} onPress={onRandomWordPress} />
</View>
)}
<View style={styles.result}>
<FlatList
data={data}
keyExtractor={(word) => word.id.toString()}
renderItem={({ item }) => (
<ListItem title={item.word} subTitle={item.descp} />
)}
ItemSeparatorComponent={ListItemSeparator}
/>
</View>
</Screen>
);
}