应用程序在每次状态更新时都会重新粉刷/重新呈现的问题

时间:2019-12-02 23:26:30

标签: javascript reactjs react-hooks

我有一个包含两个部分的图库照片应用。 “图库” 部分和“选择” 部分。 图库部分中的每张照片都有一个添加到购物车按钮。按下该特定照片后,该照片会添加到选择部分,其中有多个选项可供选择,每个选项都有一个输入框,用于输入 Quantity (数量)(如下图所示)。

我的问题是,当我键入数量(每种类型)时,应用会重新绘制,移至页面顶部,并失去对特定输入的关注。

查看此gif以查看我在说什么

photo-gallery.gif

下面是组件(不包括导入和不相关的代码)。

当按下添加到购物车时,调用handleAddSelection并导致以以下格式将照片对象添加到selectedPhotos阵列状态,然后在< strong>选择部分。

{
  "path": "/client-photos/8/Esau Silva-3678-ytht6745.jpg",
  "label": "ytht6745.jpg",
  "packages": [
    {
      "optionID": 9,
      "description": "1-11x14",
      "price": 25,
      "type": "Individual Sheets",
      "quantity": 0
    },
    {
      "optionID": 8,
      "description": "1-16x10",
      "price": 35,
      "type": "Individual Sheets",
      "quantity": 0
    },
    ...
  ]
}

当我在每个软件包选项的输入框中之一中输入数字时,我会呼叫handleUpdateSelection

import produce from 'immer';

const ClientPhotos = () => {
  const [client, setClient] = useState({});
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const [selectedPhotos, setSelectedPhotos] = useState([]);
  const { slug } = useParams();

  useEffect(() => {
        // Load photos
    const fetchClient = async () => {
        ...
        setClient(photos);
        ...
    }
  }, [slug]);

  const handleAddSelection = photo => {
    const packages = client.packageOptions.map(p => ({
      ...p,
      quantity: 0,
    }));
    const photoObject = {
      ...photo,
      packages,
    };

    setSelectedPhotos([...selectedPhotos, photoObject]);
  };

  const handleRemoveSelection = (e, photoLabel) => { ... };

  const handleUpdateSelection = (e, option, selectionIndex, packageIndex) => {
    const quantity = e.target.value;
    const { optionID } = option;

    // Get the specific photo from the state array
    const selection = selectedPhotos[selectionIndex];

    // Now update the quantity for the specific package in the selected photo
    const nextState = produce(selection, draft => {
      draft.packages[packageIndex].quantity = quantity;
    });

    // Finally update the app state array
    setSelectedPhotos(
      produce(selectedPhotos, draft => {
        draft[selectionIndex] = nextState;
      }),
    );
  };

  const Render = () => {
    return (
      <div className="container container-extended">
        <h1>{client.name} Photos</h1>
        <PhotoGallery
          photos={client.photos}
          handleAddSelection={handleAddSelection}
        />
        <PackageOptions />
      </div>
    );
  };

  const PackageOptions = () => {
    return (
      <section className="packages-content">
        <h2>Selections</h2>
        {selectedPhotos.map(({ label, packages }, selectionIndex) => (
          <Fragment key={label}>
            <div className="row">
              <div className="col-md-3">
                <p className="photo-label d-flex justify-content-between">
                  <strong>{label}</strong>
                  <button
                    type="button"
                    title="Delete Selection"
                    onClick={e => handleRemoveSelection(e, label)}
                  >
                    <i className="fas fa-trash-alt"></i>
                  </button>
                </p>
              </div>
              <div className="col">
                <Table borderless hover responsive>
                  <thead>
                    <tr>
                      <th>Package Options</th>
                      <th>Price</th>
                      <th>Quantity</th>
                    </tr>
                  </thead>
                  <tbody>
                    {packages.map((p, packageIndex) => (
                      <tr key={p.optionID}>
                        <td>{p.description}</td>
                        <td>{currencyFormatter.format(p.price)}</td>
                        <td>
                          <Input
                            className="quantity"
                            onKeyPress={numbersOnly}
                            onChange={e =>
                              handleUpdateSelection(e,p,selectionIndex,packageIndex,)
                            }
                            value={packages[packageIndex].quantity}
                          />
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </Table>
              </div>
            </div>
            <hr />
          </Fragment>
        ))}
      </section>
    );
  };

  return (
    <>
      {isError && <Alert color={alertTypes.Danger}>Something went wrong</Alert>}
      {isLoading ? <Spinner /> : <Render />}
    </>
  );
};

const processLabel = photo => { ... };
const processData = data => { ... };

然后,在每种类型的数量输入之后,整个页面都会重新呈现,从而失去对输入的关注,并移至页面顶部。

有人可以指出正确的方向吗?我不希望整个页面重新呈现,也不希望失去对特定输入的关注。

谢谢

1 个答案:

答案 0 :(得分:0)

以供将来参考

我能够通过切换到类组件来解决我的问题。我想是有问题的,我的状态数据结构无法配合。

我怀疑是因为useState替换了整个特定状态,并导致整个页面重新呈现。与this.setState合并状态相反。

现在唯一重新渲染的是输入?