我有一个应用程序,从GraphQL数据库中提取数据,然后将其映射到自定义表单组件(数量数字文本框).现在,组件本身保存着它们各自数量的状态,但我需要能够从父级访问这些值,以便我可以使用应用程序中其他地方的输入来更改数量.我已经查看了这是如何完成的,我认为这可能是我需要的,但我不知道如何应用它:[How to Target DOM with reaction useRef in map][1]

我的应用程序由一个父元素、一个包含输入的Top Bar、一个modal组件和一个从GraphQL查询填充的元素映射组成.

export default function Home() {
  const [batch, setBatch] = useState([]);
  const [target, setTarget] = useState("");
  const [batchCount, setBatchCount] = useState(0);
  const [cartModalStatus, setCartModalStatus] = useState(false);

  const elementValues = useRef([]);
  const fetcher = query => request("https://data.objkt.com/v2/graphql", query);
  const { data, error } = useSWR(
  `{
    listing(where: {token: {creators: {creator_address: {_eq: ` + target + `}}, supply: {_gt: "0"}}, status: {_eq: "active"}}, order_by: {token: {timestamp: asc}, price: asc}) {
      token {
        name
        display_uri
        timestamp
        supply
      }
      price
      seller {
        address
        alias
      }
      amount_left
    }
  }`, fetcher);

  const handleItemCount = () => {
    let count = 0;

    for (let i = 0; i < batch.length; i++)
      count += batch[i][1];

    setBatchCount(count);
  }

  const onCartClick = () => {
    setCartModalStatus(true);
  }

  const onHideModal = () => {
    setCartModalStatus(false);
  }

  const onSubmit = (e) => {
    console.log(e);
    setTarget(e.target[0].value);
    e.preventDefault();
  };

  const onChange = (el, quantity) => {
    let batchCopy = batch;
    let found = false;
    let foundIndex;

    for (var i = 0; i < batchCopy.length; i++)
      if (batchCopy[i][0] === el)
      {
        found = true;
        foundIndex = i;
      }       
    
    if (!found) batchCopy.push([el, quantity]);
    else if (found) batchCopy[foundIndex][1] = quantity

    setBatch(batchCopy);
    handleItemCount();

  };

  return (
    <Container>
      <TopBar onSubmit={onSubmit} cartTotal={batchCount} onCartClick={onCartClick}/>
      <CartModal show={cartModalStatus} onHideModal={onHideModal} batch={batch}/>
      <DataMap target={target} onChange={onChange} data={data} error={error}/>
    </Container>
  )
}

DataMap是来自查询的数据.我需要将每个元素与一个数量相匹配,这是通过在每个子元素中保留单独的状态来实现的,但我需要父元素能够访问该数量.

export function DataMap(props){
  
  const onChange = (el, quantity) => {
    console.dir(el);
    props.onChange(el, quantity);
  };

  if (props.target === "") return <div>No target.</div>;
  if (props.target !== "" && validateAddress(props.target) !== 3) return <div>Invalid address.</div>;
  if (props.error) {
    console.log(props.error);
    return <div>Failed to Load</div>;
  }
  if (!props.data) return <div>Loading...</div>;
  if (!props.error && props.data){

    return <Row>
    {props.data["listing"]
    .map((el, i , arr) => {
      return (
      <Col key={i} id={i} xs={4} sm={4} md={3} lg={2}>
          <StateImg src={"https://ipfs.io/ipfs/" + el["token"]["display_uri"].slice(7,)}/>
          <h5>{el["token"]["name"]}</h5>
          <p>{el["price"] / 1000000} {" xtz"}</p>
          <Row>
            <QuantityForm remaining={el["amount_left"]} onChange={onChange} element={el}/>
          </Row>
      </Col>)      
    })}
    </Row>
  }
}

最后,QuantityForms只是每件商品数量的表单输入.现在,状态保存在每个单独的元素中,并向上传递到父元素的"批处理"状态,但这意味着除了使用这些特定的输入之外,我不能改变数量.

export function QuantityForm(props){
  const [quantity, setQuantity] = useState(0);

  useEffect(()=>{
    props.onChange(props.element, quantity); 
  }, [props.element, quantity]);

  const onChange = (e) => {
    setQuantity(parseInt(e.target.value));  
    e.preventDefault();   
  };

  return (
    <Form.Group>
      <Form.Label>Quantity</Form.Label>
      <InputGroup>
        <Form.Control onChange={onChange} onKeyDown={(e)=>{e.preventDefault();}} type={"number"} value={quantity} min={0} max={props.remaining} aria-describedby="basic-addon1"/>
          <InputGroup.Text id="basic-addon1">
            {"/" + props.remaining}
          </InputGroup.Text>
      </InputGroup>
    </Form.Group>
  );
}

我们非常感谢在使用ref访问映射的QuantityForms值方面的任何帮助. [1]:How target DOM with react useRef in map

推荐答案

你在这里不需要裁判."react 方式"是将状态向上移动到共同的父级.因此,如果要修改QuantityFormCartModal中的数量,则应将其保留在Home组件中.

让我们用batch来表示:

const [batch, setBatch] = useState([]); // [{index, count}]

你不需要一个batchCount岁的州.只要计算一下,它是便宜的:

const batchCount = batch.reduce((sum, item) => sum + item.count, 0);

在这里,我们更新现有项目,插入新项目,并删除带有count === 0的项目:

const onChange = (index, count) => {
  if (count === 0) {
    setBatch(batch.filter((b) => b.index !== index));
  } else {
    const found = batch.find((b) => b.index === index);
    if (found) {
      setBatch(batch.map((b) => (b.index === index ? { index, count } : b)));
    } else {
      setBatch([...batch, { index, count }]);
    }
  }
};

请注意,以下内容在Reaction中不起作用,因为Object.is(batch, batchCopy) === true:

let batchCopy = batch;
...
setBatch(batchCopy);

让我们渲染Home个组件:

return (
  <div>
    <TopBar cartTotal={batchCount} />
    <DataMap data={data} batch={batch} onChange={onChange} />
    <CartModal data={data} batch={batch} onChange={onChange} />
  </div>
);

data包含所有关于产品的信息,它是non-reactive的价值.

batch只包含数量,它是reactive个值.

const TopBar = ({ cartTotal }) => {
  return (
    <div>
      <h2>TopBar</h2>
      <h3>Cart total: {cartTotal}</h3>
    </div>
  );
};
const DataMap = ({ data, batch, onChange }) => {
  return (
    <div>
      <h2>DataMap</h2>
      {data.map(({ token: { name }, price, amount_left }, index) => (
        <div key={name}>
          <div>name: {name}</div>
          <div>price: {price}</div>
          <QuantityForm
            value={batch.find((b) => b.index === index)?.count || 0}
            maxValue={amount_left}
            onChange={(v) => onChange(index, v)}
          />
        </div>
      ))}
    </div>
  );
};
const QuantityForm = ({ value, maxValue, onChange }) => {
  return (
    <div style={{ display: "flex" }}>
      {value} / {maxValue}
      <button onClick={(e) => onChange(Math.min(value + 1, maxValue))}>
        +
      </button>
      <button onClick={(e) => onChange(Math.max(value - 1, 0))}>-</button>
    </div>
  );
};
const CartModal = ({ data, batch, onChange }) => {
  return (
    <div>
      <h2>CartModel</h2>
      {batch.map(({ index, count }) => (
        <div key={index}>
          {data[index].token.name}: {count}
          <button onClick={(e) => onChange(index, 0)}>Cancel</button>
        </div>
      ))}
    </div>
  );
};

100

Reactjs相关问答推荐

sourceBuffer. appendBuffer成功,但h5视频播放器卡住了

为什么我的标签在Redux API中不能正常工作?

如何将图像(在代码中称为自己)定位在蓝色圈内的点上?我正在使用material UI和Reaction

使用Reaction-Hook-Form时未激发React.js onBlur事件

React-React中是否完全支持基于类的组件?

为什么react日历时间轴项Renderprops 不能与useState挂钩一起使用?

状态改变不会触发重新渲染

列表中的每个子项都应该有一个唯一的keyprops .在react 应用程序中

FabricJS反序列化问题

Mui Datepicker 设置了错误的日期

显示我是否加入聊天应用程序时出现问题

在 monorepo 内的工作区或库之间共享 redux 状态

是否可以直接在 jsx 标签上使用 CSS 定义的 colored颜色 ?

Firebase查询返回随机用户数据,而不是过滤后的用户数据

Stripe Checkout:是否可以创建不自动续订的订阅?

基于标记名的博客详细信息分组没有发生

Lodash 在命名导入中导入整个包

React useEffect hooks return () => cleanup() vs return cleanup

Material UI 安装和 React v. 18.2 出现问题

由于另一个子组件,如何在react 中渲染另一个子组件