useState() 函数不更新值并且 useEffect() 替代方法不起作用

问题描述 投票:0回答:1

我的 useState 钩子有问题,每当我调用 setSate 时,它都不会改变 state 的值。这个问题我在我创建的自定义 SearchBar 中得到了它,每当用户引入某些内容时 SearchBar 都会给他一些建议,就像普通的 SearchBar 一样,这是通过调用一个函数来完成的miniSearch搜索引擎搜索数据(名为“FruitsAndVeggies.js”)并将其保存在suggestions状态中,这些建议将显示在屏幕中的列表中。问题是列表没有显示,因为 suggestions 状态为空,我用 console.log() 检查了它,它似乎没有更新。有人可以帮我解决这个问题吗?

顺便说一句,我在 ProfileScreen 组件中创建了一个临时 SearchBar,它工作得很好,但在另一个组件上则不然。

这是给我带来这个问题的SearchBar

import React, { useState, useEffect } from "react";
import { View, TextInput, FlatList, Text } from "react-native";
import MiniSearch from "minisearch";
import { ScaledSheet, verticalScale, scale } from "react-native-size-matters";

/**
 * Generic SearchBar Component.
 *
 * A reusable and customizable search bar component with suggestions for React Native applications.
 *
 * @component
 * @param {boolean} [border=false] - Determines whether to show a border around the search bar.
 * @param {StyleSheet.NamedStyles} [addStyles] - Additional styles for the search bar.
 * @param {string} [placeholder="Search"] - Placeholder text displayed in the search bar.
 * @param {Function} [handleOnFocus] - Function to call when the search bar gains focus.
 * @param {Function} [onChangeText] - Function to call when the text in the search bar changes.
 * @param {Array} [data] - An array of suggestion items to display.
 * @param {Function} [onSuggestionPress] - Function to call when a suggestion item is pressed.
 * @param {Function} [keyExtractor] - Function to extract a unique key for each suggestion item. Default is 'id'
 * @returns {React.Element} Generic SearchBar component.
 */

export default function SearchBar({
  border = false,
  addStyles,
  placeholder = "Search",
  handleOnFocus,
  onChangeText,
  data,
  onSuggestionPress,
  keyExtractor = (item) => item.id,
}) {
  const [showSuggestions, setShowSuggestions] = useState(false);
  const [suggestions, setSuggestions] = useState([]);

  // MiniSearch engine for data
  const miniSearch = new MiniSearch({
    fields: ["name"],
    idField: "id",
    storeFields: ["name"],
  });

  useEffect(() => {
    if (suggestions.length > 0)
      console.log("Suggestions UseEffect: " + JSON.stringify(suggestions));
  }, [suggestions]);

  // useEffect(() => {
  //   if (data) {
  //     miniSearch.addAll(data, ["name"]);
  //     console.log("SearchBar -> Data added");
  //   }
  // }, [data]);

  // useEffect(() => {
  //   if (suggestions)
  //     console.log("Search suggestions: " + JSON.stringify(suggestions) + "\n");
  // }, [suggestions]);

  /**
   * Handles the change in the search bar text.
   *
   * @param {string} newText - The updated text in the search bar.
   */
  const handleChangeText = async (text) => {
    //checking if text is empty
    if (!text) {
      setShowSuggestions(false);
      setSuggestions([]);
      return;
    }
    let suggestion = await miniSearch.autoSuggest(text);
    if (suggestion) {
      setSuggestions(suggestion);
      setShowSuggestions(true);
      console.log(
        "SearchBar -> suggestions: " + JSON.stringify(suggestions) + "\n"
      );
    }
  };

  return (
    <View
      style={[
        styles.searchbarView,
        border && styles.searchBarBorder,
        addStyles,
      ]}
    >
      <TextInput
        style={{
          flex: 1,
          fontSize: 16,
        }}
        placeholder={placeholder}
        onChangeText={handleChangeText}
        onFocus={handleOnFocus}
        blurOnSubmit={true}
      />
      {showSuggestions && suggestions && (
        <FlatList
          data={suggestions}
          renderItem={({ item }) => (
            <View style={styles.suggestionItem}>
              <Text onPress={() => onSuggestionPress(item)}>{item.name}</Text>
            </View>
          )}
          keyExtractor={keyExtractor}
        />
      )}
    </View>
  );
}

const styles = ScaledSheet.create({
  searchbarView: {
    height: verticalScale(35),
    justifyContent: "flex-start",
    alignItems: "center",
    flexDirection: "row",
    marginTop: verticalScale(30),
    paddingHorizontal: scale(10),
    marginHorizontal: scale(20),
  },
  searchBarBorder: {
    borderColor: "#1ACE2B",
    borderWidth: 1,
    borderRadius: scale(7),
  },
  suggestionItem: {
    borderBottomWidth: 1,
    borderColor: "#e0e0e0",
    padding: verticalScale(10),
  },
});

这就是我所说的:

import { View, Text, ScrollView } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import SliderComponent from "../Components/SliderComponents";
import ProductComponent from "../Components/ProductHorizontalComp";
import Stores from "../Data/Stores";
import FruitsAndVeggies from "../Data/FruitsAndVeggies";
import Akcijos from "../Data/Akcijos";
import SearchBar from "../Components/Generic/SearchBar";

/**
 *
 * @returns Component with the main page where all the stores and the basic prodcuts are shown
 */
export default function StoreScreen({ navigation }) {
  /**
   * Header that shows the title on the left and a Button on the right
   * @param {string} titleText What will be shown on the title
   * @param {Function} handleOnPress Function that will be called when pressed on the Button
   * @param {string} buttonTitle Title of the
   * @returns
   */
  const viewAllHeader = (titleText, handleOnPress, buttonTitle = "ViewAll") => {
    return (
      <View
        style={{
          flex: 1,
          paddingTop: 20,
          flexDirection: "row",
          justifyContent: "space-between",
          alignItems: "center",
        }}
      >
        <Text
          style={{
            paddingHorizontal: 20,
            fontSize: 24,
            fontWeight: "700",
          }}
        >
          {titleText}
        </Text>
        <Text
          style={{ paddingHorizontal: 20, color: "#00da34" }}
          onPress={handleOnPress}
        >
          {buttonTitle}
        </Text>
      </View>
    );
  };

  const consl = () => console.log("Focused");

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <SearchBar
        border={true}
        handleOnFocus={consl}
        onChangeText={(text) => console.log(text)}
        data={FruitsAndVeggies}
        keyExtractor={(item) => item.name}
      />
      {/* Horizontal SrollView */}
      <ScrollView>
        {/* Horizontal scrollView of all the stores */}
        {viewAllHeader("Stores", () =>
          navigation.navigate("ViewAll", {
            array: Stores,
            title: "Stores",
            type: 0,
          })
        )}
        <ScrollView
          style={{ marginTop: 10 }}
          horizontal={true}
          showsHorizontalScrollIndicator={false}
        >
          {Stores.map((store) => (
            <SliderComponent
              key={store.id} // Make sure to provide a unique key for each SliderComponent
              name={store.name}
              image={store.Logo}
            />
          ))}
        </ScrollView>

        {/* Fruits and veggies */}
        {viewAllHeader("Fruits and Veggies", () =>
          navigation.navigate("ViewAll", {
            array: FruitsAndVeggies,
            title: "Fruits and vegetables",
            type: 1,
          })
        )}

        <ScrollView
          style={{ marginTop: 10 }}
          horizontal={true}
          showsHorizontalScrollIndicator={false}
        >
          {FruitsAndVeggies.map((item) => (
            <ProductComponent
              image={item.image}
              store={item.store}
              name={item.name}
              quantity={item.quantity}
              quantityType={item.quantityType}
              price={item.price}
              isCardDiscounted={item.isCardDiscounted}
              discountPrice={item.discountPrice}
              navigation={navigation}
              soldBy={item.soldBy}
              key={item.id}
            />
          ))}
        </ScrollView>

        {/* Actual discounts */}

        {viewAllHeader("Other discounts", () =>
          navigation.navigate("ViewAll", {
            array: FruitsAndVeggies,
            title: "Fruits and vegetables",
            type: 1,
          })
        )}

        {/* Discount Posts */}
        <ScrollView
          horizontal={true}
          showsHorizontalScrollIndicator={false}
          style={{ marginTop: 10, paddingRight: 20 }}
        >
          {Akcijos.map((item) => (
            <ProductComponent
              key={item.id}
              navigation={navigation}
              image={item.image}
              store={item.store}
              name={item.name}
              quantity={item.quantity}
              price={item.price}
              discountPrice={item.discountPrice}
              isCardDiscounted={item.isCardDiscounted}
              soldBy={item.soldBy}
            />
          ))}
        </ScrollView>
      </ScrollView>
    </SafeAreaView>
  );
}

这是运行良好的 scratch SearchBar

import React, { useState } from "react";
import { Text, TextInput, FlatList, View } from "react-native";
import MiniSearch from "minisearch";
import FruitsAndVeggies from "../Data/FruitsAndVeggies";
import genericStyles from "../Styles/genericStyles";

export function ProfileScreen() {
  const [searchResults, setSearchResults] = useState([]);
  const [suggestions, setSuggestions] = useState([]);
  const [showSuggestions, setShowSuggestions] = useState([]);

  const searchEngine = new MiniSearch({
    fields: ["name"],
    idField: "id",
    storeFields: ["name"],
  });

  searchEngine.addAll(FruitsAndVeggies, ["name"]);

  const onChangeText = (text) => {
    if (!text) {
      setSuggestions(false);
      setSearchResults([]);
      return;
    }
    let suggestions = searchEngine.autoSuggest(text);
    if (suggestions) {
      setSuggestions(suggestions);
      setShowSuggestions(true);
      console.log("Search suggestions: " + JSON.stringify(suggestions) + "\n");
    }
    // let searchResults = searchEngine.search(text);
    // if (searchResults) setSearchResults(searchResults);
  };

  return (
    <View style={{ flex: 1, ...genericStyles.genericCenteredAlignment }}>
      <TextInput
        style={{
          marginTop: "50%",
          width: "80%",
          height: 50,
          ...genericStyles.genericBorder,
        }}
        onChangeText={onChangeText}
        onBlur={() => setShowSuggestions(false)}
      />
      {showSuggestions && suggestions && (
        <View style={{ flex: 1 }}>
          <Text style={{ fontWeight: "600" }}>Suggestions</Text>
          <FlatList
            data={suggestions}
            renderItem={({ item }) => (
              <Text
                style={{ ...genericStyles.genericBorder, flex: 1 }}
                onPress={() => onSuggestionPress(item)}
              >
                {item.suggestion}
              </Text>
            )}
            keyExtractor={(item) => item.suggestion}
          />
        </View>
      )}
      {searchResults && (
        <View style={{ flex: 1 }}>
          <FlatList
            data={suggestions}
            renderItem={({ item }) => (
              <Text
                style={{ ...genericStyles.genericBorder, flex: 1 }}
                onPress={() => onSuggestionPress(item)}
              >
                {item.suggestion}
              </Text>
            )}
            keyExtractor={(item) => item.suggestion}
          />
        </View>
      )}
    </View>
  );
}

这是搜索到的数据:

const FruitsAndVeggies = [
  {
    name: "Bananai",
    quantity: 1,
    quantityType: 2,
    discountPrice: null,
    isCardDiscounted: false,
    price: 1.19,
    image:
      "https://storage.googleapis.com/download/storage/v1/b/lastmile-ui/o/images%2Fchain_products%2FIKI%2Fthumb_iNqZIWhnRVjabieALQrW.jpg?generation=1654518838165052&alt=media",
    soldBy: 2,
    store: 1,
    id: 1,
  },
  {
    name: "Batatai",
    quantity: 1,
    quantityType: 2,
    discountPrice: true,
    isCardDiscounted: false,
    price: 1.9,
    soldBy: 2,
    store: 1,
    id: 7,
  },
  {
    name: "Citrinos",
    quantity: 1,
    quantityType: 2,
    discountPrice: 1.69,
    isCardDiscounted: false,
    price: 2.49,
    image:
      "https://storage.googleapis.com/download/storage/v1/b/lastmile-ui/o/images%2Fproducts%2Fthumb_H83PY5twSuOZBcPevKFy.jpg?generation=1631695789675754&alt=media",
    soldBy: 2,
    store: 1,
    id: 2,
  },
  {
    name: "Mandarinai",
    quantity: 1,
    quantityType: 2,
    discountPrice: 1.49,
    isCardDiscounted: true,
    price: 2.99,
    image:
      "https://storage.googleapis.com/download/storage/v1/b/lastmile-ui/o/images%2Fproducts%2Fthumb_Hy7HIb8PQCGqEvATZw8b.jpg?generation=1631695783290806&alt=media",
    soldBy: 2,
    store: 1,
    id: 3,
  },
  {
    name: "Avokadai",
    quantity: 1,
    quantityType: 5,
    discountPrice: null,
    isCardDiscounted: false,
    price: 0.69,
    image:
      "https://storage.googleapis.com/download/storage/v1/b/lastmile-ui/o/import%2Fphotos%2Fconverted%2Fproduct%2Fglobal_1RA2MejrFpM61j0dpP43_cTAkEjfDTeMFXf0e6W4JKKhwXXA3_cIQ08jOtIths09SLOkmW_thumb.jpg?generation=1665477260007150&alt=media",
    soldBy: 5,
    store: 1,
    id: 4,
  },
  {
    name: "Sveriami obuoliai ŠAMPION",
    quantity: 1,
    quantityType: 2,
    discountPrice: 0.69,
    isCardDiscounted: false,
    price: 0.99,
    image:
      "https://storage.googleapis.com/download/storage/v1/b/lastmile-ui/o/images%2Fproducts%2Fthumb_hEyrjLkWqfDN7Ce8b3K5.jpg?generation=1589636330241009&alt=media",
    soldBy: 2,
    store: 1,
    id: 5,
  },
  {
    name: "Besėklės žaliosios vynuogės",
    quantity: 500,
    quantityType: 1,
    discountPrice: 1.49,
    isCardDiscounted: false,
    price: 2.49,
    image:
      "https://storage.googleapis.com/download/storage/v1/b/lastmile-ui/o/images%2Fchain_products%2FIKI%2Fthumb_hcmNGxBKsp0jcdhciqfK.jpg?generation=1654374772066054&alt=media",
    soldBy: 5,
    store: 1,
    id: 6,
  },
];

export default FruitsAndVeggies;

如您所见,我尝试了 useEffect() 挂钩,但它不起作用。

是否有任何解决方案可以让 setState 立即更新其值?

感谢您的帮助:)!!

reactjs react-native react-hooks synchronization
1个回答
0
投票

我认为从 TextInput 组件调用 onChangeText 内的所有逻辑不是一个好主意。最好让该函数只承担一项职责,那就是设置存储用户插入的文本的状态。关于该方法的另一件事是,每次用户输入一个字符或删除它时,它都会向 miniSearch 引擎发出请求,想象一下会有多少次调用,它会降低应用程序的性能,并可能会弄乱渲染和渲染之间的数据。更新周期。

如果我要编写类似的代码,我会让 onChangeText 函数仅负责设置存储键入文本的状态。之后,我将对 miniSearch 引擎进行去抖调用,这样做后,miniSearch 引擎只会在所需的延迟后被调用。我能够做到你想要的,我希望它有帮助。我知道它的 ReactJS 示例,但如果你将 input 替换为 TextInput,将 span 替换为 Text,它应该可以正常工作。

import React, { useEffect, useState } from "react";
import MiniSearch from "minisearch";
import { documents } from "./documents";

function App() {
  const [inputValue, setInputValue] = useState("");
  const [suggestions, setSuggestions] = useState([]);

  let miniSearch = new MiniSearch({
    fields: ["title", "text"],
    storeFields: ["title", "category"],
  });

  miniSearch.addAll(documents);

  const onChangeText = (event: any) => {
    setInputValue(event.target.value);
  };

  useEffect(() => {
    const timer = setTimeout(() => {
      let suggestions = miniSearch.autoSuggest(inputValue);
      setSuggestions(suggestions);
    }, 500);

    return () => clearTimeout(timer);
  }, [inputValue]);

  return (
    <>
      {suggestions.map((current) => (
        <span key={current.score}>{current.suggestion}</span>
      ))}

      <br></br>
      <input placeholder="Type here" onChange={onChangeText} />
    </>
  );
}

export default App;

没有什么会立即改变,每个生命周期方法之间都有很多渲染和更新周期。有时我们会弄乱渲染之间的数据。

© www.soinside.com 2019 - 2024. All rights reserved.