【问题标题】:How do I set state of selected values from a two-dimensional array with a form.select?如何使用 form.select 从二维数组中设置选定值的状态?
【发布时间】:2022-12-25 06:10:56
【问题描述】:

当我使用 Form.Select 时,我在从 useState 产品获取值 price 时遇到了一些问题。

这个想法是,使用Form.Select,您可以通过名称选择产品,然后onChangeProduct 将使用给定的产品名称和该产品的价格属性设置data 的状态。我只是不确定如何获取 price 值而不需要用户在 Form.Select 中手动选择价格。

该代码是我目前获得的代码的简单说明。

如果您有任何组件可以使这比我更容易听到它。

希望这是有道理的。

const [data, setData] = useState([]);

const [product, setProduct] = useState([
    { name: "TV", price: 1000 },
    { name: "Phone", price: 3000 },
  ]);

const onChangeProduct = (name, value) => {
   setData((prevValue) => {
      const newValues = [...prevValues];
      newValues = Object.assign({}, newValues, {[name]: value });
      return newValues;
   });
};

return (
<Form>
   <Form.Select
   onChange={(event) => {onChangeProduct("name", event.target.value);}}
   value={product.name}
   name="product"
   >
      {product.map((item) => {
         return <option value={item.name}>{item.name}</option>;
      })}
   </Form.Select>
</Form>
);

编辑

我决定采用另一种方式来完成这项工作。我可能需要将产品设置为数组/状态,因为我希望以后能够同时了解价格和产品。

这个想法是让系统可以在卡上添加/删除产品,您可以在卡上使用产品名称和价格检查结帐。因此,您可以选择一个产品列表,并以这种方式查看该产品的价格属性。

我使用了在这里获得帮助的相同功能:onChange, onProductRemove and add function

CardForm.jsx -> ModalEditProducts.jsx -> ProductEdit.jsx

CardForm.jsx 中,我检查是否有 ID,如果有 ID,我将获取该用户已选择的所有产品。

CardForm.jsx

const CardForm = () => {
  const [data, setData] = useState({});

  const { id } = useParams();

  const [products, setProducts] = useState([]);

  useEffect(() => {
    if (id) {
      const fetchData = async () => {
        const docRef = doc(db, "user", id);
        try {
          const docSnap = await getDoc(docRef);
          setData(docSnap.data());
        } catch (error) {
          console.log(error);
        }
      };
      fetchData().catch(console.error);
    }
  }, []);

  const handleProductChanged = (product) => {
    setData((data) => ({ ...data, product }));
  };

  return (
    <>
      <Container className="mb-3 content_container_primary">
        <Row>
          <Col xs={12} md={12} lg={8} className="">
            <Form>
              <div className="box content_pa">
                <Col xs={12} md={12} lg={12}>
                  <div>
                    <ModalEditProducts
                      onProductChanged={handleProductChanged}
                      data={data.product ?? []}
                      title="Edit products"
                    />
                  </div>
                </Col>
              </div>
            </Form>
          </Col>
        </Row>
      </Container>
    </>
  );
};

ModalEditProducts.jsx

const ModalEditProducts = ({ title, data, onProductChanged }) => {
  const [show, setShow] = useState(false);
  const [newData, setNewData] = useState([]);

  useEffect(() => {
    setNewData(data);
  }, [data, show]);

  const handleClose = () => setShow(false);
  const handleShow = () => setShow(true);

  const handleProductChange = (index, name, value) => {
    setNewData((prevValues) => {
      const newValues = [...prevValues];
      newValues[index] = Object.assign({}, newValues[index], { [name]: value });
      return newValues;
    });
  };

  const handleProductAdded = () => {
    setNewData((prevValues) => [...prevValues, { product: "", price: 0 }]);
  };

  const handleProductRemoved = (index) => {
    setNewData((prevValues) => {
      const newValues = [...prevValues];
      newValues.splice(index, 1);
      return newValues;
    });
  };

  const handleSubmitProducts = (e) => {
    e.preventDefault();
    onProductChanged(newData);
    setShow(false);
  };

  return (
    <>
      <div className="content_header">
        <div className="content_header_top">
          <div className="header_left">Products</div>
          <div className="header_right">
            <Button className="round-btn" onClick={handleShow}>
              <i className="fa-solid fa-pencil t-14"></i>
            </Button>
          </div>
        </div>
      </div>

      {show && (
        <Modal show={show} onHide={handleClose} size="">
          <Modal.Header closeButton>
            <Modal.Title>{title}</Modal.Title>
          </Modal.Header>
          <Modal.Body>
            <ProductEdit
              data={newData}
              onProductChanged={handleProductChange}
              onProductAdded={handleProductAdded}
              onProductRemoved={handleProductRemoved}
            />
          </Modal.Body>
          <Modal.Footer>
            <Form>
              <Button
                className="btn-skill-complete"
                onClick={handleSubmitProducts}
              >
                Save
              </Button>
            </Form>
          </Modal.Footer>
        </Modal>
      )}
    </>
  );
};

ProductEdit.jsx

const ProductEdit = ({ data, onProductChanged, onProductRemoved, onProductAdded }) => {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    GetProducts(setProducts);
  });

  return (
    {data.length > 0 ? (
        <Row>
          <Col xs={9} md={9}>
            <div className="product-modal-title mb-3">Pick a new product</div>
          </Col>
          <Col xs={3} md={3}>
            <div className="product-modal-title mb-3 text-center">
              Remove/add
            </div>
          </Col>
        </Row>
      ) : null}

      {data.length === 0 ? (
        <Col xs={12} md={12}>
          <Button
            className="btn-st-large t-16 "
            type="button"
            onClick={onProductAdded}
          >
            Add a product
          </Button>
        </Col>
      ) : (
        <>
          {data?.map((inputField, index) => (
            <div key={index}>
              <Row>
                <Col xs={9} md={9}>
                  <Form.Select
                    as={Col}
                    className="mb-3"
                    onChange={(event) => {
                      onProductChanged(index, "product", event.target.value);
                    }}
                    id="product"
                    name="product"
                    value={inputField.product}
                  >
                    <option>Choose product</option>
                    {products.map((item) => {
                      return (
                        <option key={item.id} value={item.name}>
                          {item.name} ({item.price} kr.)
                        </option>
                      );
                    })}
                  </Form.Select>
                </Col>
                <Col xs={3} md={3}>
                  <div className="btn-section">
                    <button
                      type="button"
                      className="round-btn"
                      onClick={onProductAdded}
                    >
                      <i className="fa-solid fa-plus"></i>
                    </button>
                    <button
                      type="button"
                      className="round-btn"
                      onClick={() => onProductRemoved(index)}
                    >
                      <i className="fa-solid fa-minus"></i>
                    </button>
                  </div>
                </Col>
              </Row>
            </div>
          ))}
        </>
      )}
    </Form>
  );
};

【问题讨论】:

  • 为什么不将选项值设置为“item”而不是“item.name”,这样您的 onChange 就可以访问整个对象?
  • 那应该可以工作。我没想过只添加该项目,但问题是我无法弄清楚如何在 onChangeProduct 中获取 price 值。我很新的反应所以任何帮助表示赞赏
  • 如果您将 console.log(value) 添加到您的 onChangeconsole.log(JSON.stringify(value)),您会看到什么?我假设您会从 value.price 或类似的东西获取价格。
  • 这样做我只会在控制台中得到一个空数组[object Object]。我试图通过捕获 value.price 来访问它,但失败了。如果我想获取价格和名称的值,似乎无法捕获 item

标签: reactjs multidimensional-array react-select


【解决方案1】:

首先,产品看起来是静态的,永远不会改变。因此无需将它们保持在状态,因为永远不需要更改它们。如果它在某个时候发生变化,您只需要保持状态不变。您可以将它们保留在组件之外并引用它们。轻微的性能改进,但也向其他查看您的代码的人发出信号,它们是不可更改的和静态的。

其次,更改处理程序过于复杂。在这里,您存储的不是对象数组,而是一个简单的选定值(选定项的名称)。 React Bootstrap 仅在 event 中向您传递选项的 value,而不是整个对象。由于该值只是一个普通字符串,在 JS 中被视为“原始”,因此您无需担心任何不可变的状态更改。当您将字符串传递给函数时,字符串会被复制。但是如果你只有名字,如何得到价格呢?

要获取价格,您可以使用所选名称查找相关选项并获取价格属性。 find 是一个有用的工具。它允许您根据您定义的某些真实性在数组中查找单个项目。

您在问题中建议也将价格存储在状态中;你可能想知道为什么我不这样做。实际上在这里,您别无选择,因为库只将optionvalue 传递给您。但是,其他图书馆会将整个选项传递给您。尽管如此,一般来说,仅存储最低限度的数据以从源数据中识别选择会更好,因为您可以避免围绕使项目与源数据同步而出现的一系列错误。在这里,您的选项数据不发生变化并不重要,但这只是一种很好的做法。最好使用唯一标识符在规范源数据中查找它。

我还添加了一个默认的“请选择”选项。默认状态先选择这个(空字符串)

注意 price 在用户选择选项之前将变为 undefined,因为默认的 "" 选项不会出现在 products 中。 find 返回 undefined 如果它遍历数组并且根据传递的条件它循环的任何项目都不为真。

这就是为什么我在访问价格时在 find 之后使用 ? (optional chaining) 的原因。否则你会得到一个错误。


const products = [
    { name: "TV", price: 1000 },
    { name: "Phone", price: 3000 },
]

const MyForm = () => {
    const [selectedName, setSelectedName] = useState("");

    const price = products.find(product => product.name === selectedName)?.price

    return (
        <Form>
           <Form.Select
               onChange={(event) => {setSelectedName(event.target.value);}}
               value={selectedName}
           >
              <option value="">Select option...</option>
              {products.map((item) => {
                 return <option value={item.name}>{item.name}</option>;
              })}
           </Form.Select>
        </Form>
    );
}

【讨论】:

  • 如果我想显示单个产品的价值,这很有效,谢谢。但是我发现我需要对功能进行一些更改。我想要/需要创建一个功能,用户可以在其中添加/从他们的卡中删除产品,因此价格功能应该捕捉具有特定索引的产品的价格。我使用了你上次在 old post 给我的帮助 - 我也在这里进行了编辑。请告诉我我是否仍然可以使用这个答案,或者我是否需要另辟蹊径。
  • 呀!应该没问题。但是您还需要在范围内移动查找。我会编辑我的答案
  • 其实你也可以显示父组件和所选产品的存储位置吗?
  • 你能在这里找到问题吗?我来回试过,但还没能解决这个问题:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-14
  • 2020-02-14
相关资源
最近更新 更多