无法从React Native中的父组件更新子组件的属性

时间:2020-09-06 00:52:34

标签: javascript android ios react-native expo

我是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);
  };

执行此操作的正确方法是什么,或者我的实施有什么问题?

谢谢!

2 个答案:

答案 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>
  );
}