ep8*_*p84 7 children parent reactjs react-component use-ref
我有一个应用程序,可以从 GraphQL 数据库中提取数据,然后将其映射到自定义表单组件(数量文本框)中。现在,组件本身保存着各自数量的状态,但我需要能够从父级访问这些值,以便我可以使用应用程序中其他地方的输入来更改数量。我已经查看了这是如何完成的,我认为这可能是我需要的,但我不知道如何应用它:[How to target DOM with React useRef in map][1]
我的应用程序由一个父元素和一个包含输入的顶部栏、一个模态组件以及从 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>
)
}
Run Code Online (Sandbox Code Playgroud)
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>
}
}
Run Code Online (Sandbox Code Playgroud)
最后,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>
);
}
Run Code Online (Sandbox Code Playgroud)
非常感谢任何使用 Refs 访问映射的 QuantityForms 值的帮助。[1]:如何在地图中使用 React useRef 来定位 DOM
你在这里不需要裁判。“React 方式”是将状态移至公共父级。因此,如果您想修改数量QuantityForm和 in 的数量CartModal,那么您应该将其保留在Home组件中。
让我们使用batch它:
const [batch, setBatch] = useState([]); // [{index, count}]
Run Code Online (Sandbox Code Playgroud)
你不需要一个状态batchCount。算算,便宜:
const batchCount = batch.reduce((sum, item) => sum + item.count, 0);
Run Code Online (Sandbox Code Playgroud)
在这里,我们更新现有项目,插入新项目并删除那些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 }]);
}
}
};
Run Code Online (Sandbox Code Playgroud)
请注意,以下内容在 React 中不起作用,因为Object.is(batch, batchCopy) === true:
let batchCopy = batch;
...
setBatch(batchCopy);
Run Code Online (Sandbox Code Playgroud)
让我们渲染一下Home组件:
return (
<div>
<TopBar cartTotal={batchCount} />
<DataMap data={data} batch={batch} onChange={onChange} />
<CartModal data={data} batch={batch} onChange={onChange} />
</div>
);
Run Code Online (Sandbox Code Playgroud)
data包含有关产品的所有信息,它是是非反应值。
batch仅包含数量,并且是无功值。
const TopBar = ({ cartTotal }) => {
return (
<div>
<h2>TopBar</h2>
<h3>Cart total: {cartTotal}</h3>
</div>
);
};
Run Code Online (Sandbox Code Playgroud)
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>
);
};
Run Code Online (Sandbox Code Playgroud)
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>
);
};
Run Code Online (Sandbox Code Playgroud)
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>
);
};
Run Code Online (Sandbox Code Playgroud)