简体   繁体   English

从父级访问子级 state

[英]Accessing child state from parent

Background背景

I'm building an app which has at some point a FlatList which renders products.我正在构建一个应用程序,该应用程序在某些时候具有呈现产品的 FlatList。 The code for the list looks like this:列表的代码如下所示:

<FlatList
                data={data}
                renderItem={({ item }) => (
                    <View style={styles.container}>
                        <View style={styles.left}>
                            <Text style={styles.title}>{item.name}</Text>
                            <Text style={styles.description}>{item.description}</Text>
                            <Text style={styles.price}>${item.price}</Text>
                            <Counter />
                        </View>
                        <Image style={styles.right} source={{uri: item.image}}/>
                    </View>
                )}
            /> 

The data for this list is brought over from a Google Cloud Firestore document.此列表的数据来自 Google Cloud Firestore 文档。 Within this list you can see a component called Counter , its job is to allow the user to add and delete products from their cart.在这个列表中,您可以看到一个名为Counter的组件,它的工作是允许用户从他们的购物车中添加和删除产品。 This is its code:这是它的代码:

export default function Counter () {  


    const [count, setCount] = useState(0);

      const handleAddition=()=>{
        setCount(count + 1)
      }

      const handleDeletion=()=>{
        {count === 0 ? setCount(count) : setCount(count - 1)}
      }
return ( 
    
    <View style={styles.adder}>
        <TouchableOpacity onPress={() => {handleDeletion()}}>
            <Text style={styles.less}>-</Text>
        </TouchableOpacity>
        <Text style={styles.counter}>{count}</Text>
        <TouchableOpacity onPress={() => {handleAddition()}}>
            <Text style={styles.more}>+</Text>
        </TouchableOpacity>
    </View>

)
}

Problem问题

As you can see from the fact that I'm rendering the counter within a FlatList, I need to keep the state stored in the child rather than in the parent, as having the count in the parent would mean that if the user selects one product, every item is added at the same time.从我在 FlatList 中呈现计数器这一事实可以看出,我需要将 state 存储在子级而不是父级中,因为在父级中计数意味着如果用户选择一种产品,同时添加每个项目。

I need to have the a button show up when the user selects a product that allows them to navigate to their purchase summary and also I need that button to display the total cost of their selection and amount of products chosen.当用户选择允许他们导航到购买摘要的产品时,我需要显示一个按钮,并且我需要该按钮来显示他们选择的总成本和选择的产品数量。 As you might imagine, I've no idea how to access the child's state in the parent component.正如您可能想象的那样,我不知道如何在父组件中访问孩子的 state。

So to sum it all up: I have a child with a state update that I need to access from its parent, but I do not know how to do it.所以总结一下:我有一个孩子,我需要从它的父母那里访问 state 更新,但我不知道该怎么做。

Question ¨问题¨

Is there any way to listen to event changes in a child's state or passing it up as a prop or something like that?有什么方法可以监听孩子的 state 中的事件变化或将其作为道具传递或类似的东西?

Thanks a lot in advance!提前非常感谢!

Extra information额外的信息

用户界面

This is image shows the UI of the screen.这是显示屏幕 UI 的图像。 When pressing the "+" button it updates the count +1 and it should also display a button showing the info I mentioned before.当按下“+”按钮时,它会更新计数 +1,它还应该显示一个按钮,显示我之前提到的信息。

In renderItem you can pass method callback in here在 renderItem 你可以在这里传递方法回调

<Counter onPressFunctionItem={(isPlus) => { // handle from parent here }} />

export default function Counter ({ onPressFunctionItem }) {  


    const [count, setCount] = useState(0);

      const handleAddition=()=>{
        setCount(count + 1)
        if (onPressFunctionItem) {
          onPressFunctionItem(true)
        }
      }

      const handleDeletion=()=>{
        {count === 0 ? setCount(count) : setCount(count - 1)}
        if (onPressFunctionItem) {
          onPressFunctionItem(false)
        }
      }
   return (...)
}

Final Output:最终 Output:

在此处输入图像描述

You don't really need to pass the child component's state to the parent to achieve the same result, you can do that very easily the conventional way.您实际上不需要将子组件的 state 传递给父组件即可达到相同的结果,您可以通过常规方式轻松完成。

Here is the source code of above example:以下是上述示例的源代码:

export default function App() {
  const [products, setProducts] = useState(data);

  /* 
  with this function we increase the quantity of 
  product of selected id
  */
  const addItem = (item) => {
    console.log("addItem");
    let temp = products.map((product) => {
      if (item.id === product.id) {
        return {
          ...product,
          quantity: product.quantity + 1,
        };
      }
      return product;
    });

    setProducts(temp);
  };

  /* 
  with this function we decrease the quantity of 
  product of selected id, also put in the condition so as 
  to prevent that quantity does not goes below zero
  */
  const removeItem = (item) => {
    console.log("removeItem");
    let temp = products.map((product) => {
      if (item.id === product.id) {
        return {
          ...product,
          quantity: product.quantity > 0 ? product.quantity - 1 : 0,
        };
      }
      return product;
    });
    setProducts(temp);
  };

  /*
   this varible holds the list of selected products.
  if required, you can use it as a seperate state and use it the 
  way you want
   */
  let selected = products.filter((product) => product.quantity > 0);

  /**
   * below are two small utility functions,
   * they calculate the total itmes and total price of all
   * selected items
   */
  const totalItems = () => {
    return selected.reduce((acc, curr) => acc + curr.quantity, 0);
  };
  const totalPrice = () => {
    let total = 0;
    for (let elem of selected) {
      total += elem.quantity * elem.price;
    }
    return total;
  };

  useEffect(() => {
    console.log(products);
  }, [products]);

  return (
    <View style={styles.container}>
      <FlatList
        data={products}
        renderItem={({ item }) => {
          return (
            <Card style={styles.card}>
              <View style={styles.textBox}>
                <Text>{item.name}</Text>
                <Text>$ {item.price.toString()}</Text>
                <View style={{ flexDirection: "row" }}></View>
                <View style={styles.buttonBox}>
                  <Button
                    onPress={() => removeItem(item)}
                    title="-"
                    color="#841584"
                  />
                  <Text>{item.quantity.toString()}</Text>
                  <Button
                    onPress={() => addItem(item)}
                    title="+"
                    color="#841584"
                  />
                </View>
              </View>
              <Image
                style={styles.image}
                source={{
                  uri: item.image,
                }}
              />
            </Card>
          );
        }}
      />

      <View style={{ height: 60 }}></View>

      {selected.length && (
        <TouchableOpacity style={styles.showCart}>
          <View>
            <Text style={styles.paragraph}>
              {totalItems().toString()} total price ${totalPrice().toString()}
            </Text>
          </View>
        </TouchableOpacity>
      )}
    </View>
  );
}

You can find the working app demo here: Expo Snack您可以在此处找到工作应用演示: Expo Snack

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM