从列表中删除后如何重新渲染我的屏幕?

时间:2021-01-25 10:18:12

标签: javascript typescript firebase react-native

您好,我一直在查看有关 stackoverflow 的几个线程,但我一直无法解决我的问题。我有一个应用程序,您可以在其中将电影保存到监视列表。在这个特定的屏幕上,我想显示一个用户监视列表,并让他们能够从列表中删除它。目前该功能确实从列表中删除电影并将其从 firebase 中删除,但我无法让我的屏幕重新渲染以直观地表示删除。

这是现在的代码:


export default function MovieWatchlistTab(props: any) {
  let { movies } = props;
  let movieWatchlist: any[] = [];
  const [watchlistSnapshot, setWatchlistSnapshot] = useState();
  const user: firebase.User = auth().currentUser;
  const { email } = user;
  const watchlistRef = firestore().collection("Watchlist");

  useEffect(() => {
    getWatchlistSnapshot();
  }, []);

  const getWatchlistSnapshot = async () => {
    setWatchlistSnapshot(await watchlistRef.where("userId", "==", email).get());
  };

  const convertDataToArray = () => {
    const convertedMovieList = [];
    for (let movie in movies) {
      let newMovie = {
        backdrop: movies[movie].backdrop,
        overview: movies[movie].overview,
        release: movies[movie].release,
        title: movies[movie].title,
      };
      convertedMovieList.push(newMovie);
    }
    movieWatchlist = convertedMovieList;
  };

  const renderMovieList = () => {
    convertDataToArray();
    return movieWatchlist.map((m) => {
      const handleOnPressDelete = () => {
        const documentRef = watchlistRef.doc(watchlistSnapshot.docs[0].id);
        const FieldValue = firestore.FieldValue;
        documentRef.set(
          {
            movies: {
              [m.title]: FieldValue.delete(),
            },
          },
          { merge: true }
        );
        movieWatchlist.splice(
          movieWatchlist.indexOf(m),
          movieWatchlist.indexOf(m) + 1
        );
        console.log("movieWatchlist", movieWatchlist);
      };

      const swipeButtons = [
        {
          text: "Delete",
          color: "white",
          backgroundColor: "#b9042c",
          onPress: handleOnPressDelete,
        },
      ];
      return (
        <Swipeout right={swipeButtons} backgroundColor={"#18181b"}>
          <View key={m.title} style={{ marginTop: 10, flexDirection: "row" }}>
            <Image
              style={{ height: 113, width: 150 }}
              source={{
                uri: m.backdrop,
              }}
            />
            <View>
              <Text
                style={{
                  flex: 1,
                  color: "white",
                  marginLeft: 10,
                  fontSize: 17,
                }}
              >
                {m.title}
              </Text>
              <Text style={{ flex: 1, color: "white", marginLeft: 10 }}>
                Release: {m.release}
              </Text>
            </View>
          </View>
        </Swipeout>
      );
    });
  };

  return (
    <View
      style={{
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
        backgroundColor: "#18181b",
      }}
    >
      <ScrollView
        style={{ flex: 1 }}
        contentContainerStyle={{
          width: Dimensions.get("window").width,
        }}
      >
        {renderMovieList()}
      </ScrollView>
    </View>
  );
}


我一直在尝试使用 useStates,我认为答案是朝这个方向发展,但无论如何我似乎无法让它发挥作用。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

您的代码中有几行显示了对 React 状态的误解。您有一个值 let movieWatchlist: any[] = [];,您在 convertDataToArray() 中重新分配并在 handleOnPressDelete 中改变。这不是我们在 React 中做事的方式,它不会正确触发更新。 movieWatchlist 要么需要是使用 useState 创建的有状态变量。

通过 movies 传入的 props 是否发生变化?如果是这样,那么您无需将它们存储在此处的 state 中。您可以只从 return array 一个 convertDataToArray() 而不是设置变量并返回 void

老实说,现在还不清楚 convertDataToArray 在做什么,因为看起来 newMovie 与原始电影对象相同,或者是原始电影对象的子集。如果重点是删除除这四个属性之外的其他属性,那实际上并不需要。如果 prop movies 已经是一个数组,只需删除整个函数并直接使用 movies。如果是键控对象,请使用 Object.values(movies) 将其作为数组获取。

对于我们从 props 获得什么以及我们从 firebase 获得什么,我感到非常困惑。看起来我们希望在删除后更新快照状态,但您在挂载时只运行一次 useEffect

你可能仍然有错误,但这段代码应该是一个改进:

interface Movie {
  backdrop: string;
  overview: string;
  release: string;
  title: string;
}

const MovieThumbnail = (props: Movie) => (
  <View key={props.title} style={{ marginTop: 10, flexDirection: "row" }}>
    <Image
      style={{ height: 113, width: 150 }}
      source={{
        uri: props.backdrop
      }}
    />
    <View>
      <Text
        style={{
          flex: 1,
          color: "white",
          marginLeft: 10,
          fontSize: 17
        }}
      >
        {props.title}
      </Text>
      <Text style={{ flex: 1, color: "white", marginLeft: 10 }}>
        Release: {props.release}
      </Text>
    </View>
  </View>
);

export default function MovieWatchlistTab() {
  const [watchlistSnapshot, setWatchlistSnapshot] = useState<DocumentSnapshot>();
  const user: firebase.User = auth().currentUser;
  const { email } = user;
  const watchlistRef = firestore().collection("Watchlist");

  const getWatchlistSnapshot = async () => {
    const results = await watchlistRef.where("userId", "==", email).get();
    setWatchlistSnapshot(results.docs[0]);
  };

  useEffect(() => {
    getWatchlistSnapshot();
  }, []);

  const deleteMovie = async (title: string) => {
    if ( ! watchlistSnapshot ) return;
    const documentRef = watchlistRef.doc(watchlistSnapshot.id);
    const FieldValue = firestore.FieldValue;
    await documentRef.set(
      {
        movies: {
          [title]: FieldValue.delete()
        }
      },
      { merge: true }
    );
    // reload watch list
    getWatchlistSnapshot();
  };

  // is this right?  I'm just guessing
  const movies = ( watchlistSnapshot ? watchlistSnapshot.data().movies : []) as Movie[];

  return (
    <View
      style={{
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
        backgroundColor: "#18181b"
      }}
    >
      <ScrollView
        style={{ flex: 1 }}
        contentContainerStyle={{
          width: Dimensions.get("window").width
        }}
      >
        {movies.map((m) => (
          <Swipeout
            right={[
              {
                text: "Delete",
                color: "white",
                backgroundColor: "#b9042c",
                // need to pass the title to the delete handler
                onPress: () => deleteMovie(m.title)
              }
            ]}
            backgroundColor={"#18181b"}
          >
            <MovieThumbnail {...m} />
          </Swipeout>
        ))}
      </ScrollView>
    </View>
  );
}