状态更改后失去对输入的关注

Focus on input is lost after state change

每次我重新渲染和更新状态时,我的输入框都会失去焦点。我想知道如何在用户点击离开之前将焦点保持在文本框上。

我的应用程序输入每个产品的 material 成本并计算该产品的总和。我希望在更新输入后重新呈现组件以显示更新后的总数。貌似每次material cost变化的时候总算出来了,state也在更新,但是用户每次都得手动重新选择输入框,因为焦点没了。

这是我的应用程序的屏幕截图(为简单起见,删除了 table 中元素的一些代码)

我不确定这对问题是否必要,但这就是数据的样子。在这个例子中,我保存了数组的第一个元素。

export let data = 
    {
        name: "Name",
        description:
            "",
        products: [
            {
                id: 1,
                name: "Name 1",
                material: 1.05,
                time: 25,
                total: 0,
            },
            {
                id: 2,
                name: "Name 2",
                material: 3,
                time: 252,
                total: 0,
            },
        ],
    }

这是父组件。它具有主要的渲染功能以及 setTotal 和 setMaterial 功能。通过每个 productData.products 渲染映射,以便子组件可以渲染它们。

setTotal 和 setMaterial 函数几乎相同(我确信有一种方法可以重构它)。用户在元素中键入数字后,将调用这两个函数。


function CompareCard({ type, labor }) {

    const [productData, setProductData] = useState(data);

    const setTotal = (id, total) => {
        const productPrevious = productData.products.find(function (rec) {
            return rec.id === id;
        });

        const productUpdated = {
            ...productPrevious,
            total: total,
        };

        const productNew = productData.products.map(function (rec) {
            return rec.id === id ? productUpdated : rec;
        });

        const productFullNew = {
            ...productData,
            products: productNew,
        };0

        //calculate new final totals
        let newFinalTotal = 0;
        let newFinalMaterial = 0;
        let newFinalTime = 0;
        productFullNew.products.map((product) => {
            newFinalTotal += product.total;
            newFinalMaterial += product.material;
            newFinalTime += product.time;
        });

        productFullNew.finalTotal.total = newFinalTotal;
        productFullNew.finalTotal.material = newFinalMaterial;
        productFullNew.finalTotal.time = newFinalTime;

        setProductData(productFullNew);
    };

    const setMaterial = (id, materialCost) => {
        const productPrevious = productData.products.find(function (rec) {
            return rec.id === id;
        });

        const productUpdated = {
            ...productPrevious,
            material: materialCost,
        };

        const productNew = productData.products.map(function (rec) {
            return rec.id === id ? productUpdated : rec;
        });

        const productFullNew = {
            ...productData,
            products: productNew,
        };

        setProductData(productFullNew);
    };
    return (
        <div className="Card">
            <h3> {productData.name} </h3>
            <p>{productData.description}</p>
            <table className="table">
                <thead>
                    <tr>
                        
                        <th scope="col">
                            <p>
                                <b> Material </b>
                            </p>
                        </th>
                        
                        <th scope="col">
                            <p>
                                <b> Labor </b>
                            </p>
                        </th>
                        <th scope="col">
                            <p>
                                <b> Total</b>
                            </p>
                        </th>
                    </tr>
                </thead>
                <tbody id={`${productData.name}`}>
                    {productData.products.map((product) => {
                        return (
                            <Products
                                key={product.id}
                                product={product}
                                labor={3}
                                setMaterial={setMaterial}
                                setTotal={setTotal}
                            />
                        );
                    })}
                </tbody>
            </table>
        </div>
    );
}

export default CompareCard;

这是子元素。它单独处理每个产品。

元素住在这里。

import React, { useState, useEffect } from "react";
function Products({ product, labor, setMaterial, setTotal }) {
    function setMaterialState(newMaterial) {
        product.material = newMaterial;
    }

    function calcTotal() {
        product.total = labor + product.material;
    }
    

    if (product.total === 0) {
        calcTotal();
    }

    useEffect(() => {
        setTotal(product.id, product.total);
    }, [product.total]);

    useEffect(() => {
        setMaterial(product.id, product.material);

        //setTotal(product.id, product.material + labor);
    }, [product.material]);

    function ProductMaterial() {
        const [name, setName] = useState("");

        function handleChange(e) {
            setName(parseFloat(e.target.value));
            setMaterialState(parseFloat(e.target.value));
            setMaterial(product.id, product.material);
            calcTotal();
            setTotal(product.id, product.total);
        }

        return (
            <input
                type="number"
                name="firstName"
                onChange={handleChange}
                value={product.material}
            />
        );
    }

    return (
        <tr key={product.name}>
            
            <td>
                <ProductMaterial product={product} />{" "}
    
            <td id={`total-${product.id}`}>{product.total}</td>
        </tr>
    );
}

export default Products;

我发现 Sergi Juanati 回答了一个类似的 Whosebug 问题,它对我有用 In React ES6, why does the input field lose focus after typing a character?

我将保存输入 return 语句的函数切换为以下语法:

const Products = ({ product, labor, setMaterial, setTotal }) => {};

以及对此函数的调用:

{ProductMaterial({ product: product })}