当前位置: 首页 > 知识库问答 >
问题:

我应该为每个属性使用单独的使用状态吗

陈晟睿
2023-03-14

我写了以下代码:

function CreateModal({toggleCreatePorjectModal, fetchProjects}) {
    const [formObj, setFormObj] = useState({name: "", link: "", error: ""})


    function validateLinkAndCreateProject() {
        if(formObj.link.trim() === "" || formObj.name.trim() === "") {
            setFormObj(state => ({...state, error: "ALL fields are mandatory, please enter Project Name and Feed Link to create project."}))
            return
        }
        
        rssFeedParser(formObj.link.trim())
            .then((res) => {
                axios.post(`${ServerPath}/projects/create`, {
                    name: formObj.name, 
                    link: formObj.link
                })
                .then(response => {
                    if(response.data.error) {
                        setFormObj(state => ({...state, error: "Something went wrong. Please try again after some time."}))
                    } else {
                        fetchProjects()
                        toggleCreatePorjectModal(false)
                    }  
                })
                .catch((err) => {
                    setFormObj(state => ({...state, error: err.msg}))
                })
            })
            .catch((err) => {
                setFormObj(state => ({...state, error: err.msg})) 
            })
    }
    return  (
        <div >
            {/*Will Render Something based on above code*/}
        </div>
    )
}

我只使用了一个useState在一个对象中存储“name”、“link”、“error”等属性。因为我想将FormObj和ValidateLink的逻辑保持在一起。所有三个属性仅使用一个useEffect。因此,我认为最好将所有三个属性都保留在useState中,而不是创建3个不同的useState。

但是我的经理和技术架构师告诉我要为每个属性创建3个useState,一个useState,即为名称、链接和错误创建单独的useState。根据他们的说法,永远不要在useState内部创建像useState这样的对象({name:,link:,error:})。我应该始终为每个属性创建单独的useState。

根据我的理解,在旧的react中,我们过去只有一个状态,在一个对象中有30到40个属性。随着React钩子的引入,按照“关注点分离”,我们假设将相关逻辑与代码的其余部分分离。因此,我们将得到5到6个状态,它们的对象具有4到5个属性。

注意:这里的"分离关注点"是指以这样的方式打破类代码,依赖状态use效应useRef等保持在一起。

挂钩允许您根据相关部分(例如设置订阅或获取数据)将一个组件拆分为较小的函数,而不是根据生命周期方法强制拆分

我想我错过了什么吗??,可以创建useState({name:",link:",错误: ""}). 还是应该为它们中的每一个创建单独的状态?

共有3个答案

狄承望
2023-03-14

的确,useState完全替换了状态,但是您可以创建自己的自定义挂钩,您可以尝试使用spread操作符更新状态,例如{…obj,…values},这样您就不必更新整个对象,spread操作符将负责。

我想你可以选择定制挂钩并管理它。

白云
2023-03-14

我知道这是一个老问题,但是如果有人想解决这个问题,我想说我已经用这种方法构建了一个完整的生产应用程序。每个页面(它是一个next.js的应用程序)使用一个useState,在它是一个有许多属性的反对。错误,表单数据,加载等。

如果您阅读了react文档,他们建议您不要使用react文档,因为每次都会重新评估对象,这可能会导致性能问题。即使这样,应用程序运行速度也很快。我觉得我们太过复杂了。当然,减速机会更好。但是,通过保持简单而不陷入抽象复杂性的开发速度比人们想象的要少得多的bug和性能问题。记住,反应团队是聪明的。他们不会让你失败的。如果它真的以一种很好的方式阻碍了性能,并且像useState一样是一件基本的事情,那么他们会特意阻止你做这些事情。

顺便说一句,这个制作应用程序每周为数百名订购食物的活跃用户提供服务,我们没有抱怨,只听到好消息。

巢靖
2023-03-14

这是一个判断调用,但是在大多数情况下,是的,对这三条状态信息中的每一条使用单独的useState调用(或者使用useReucer代替)。这样,设置它们就简单明了了。例如:

const [name, setName] = useState("");
const onNameChange = e => setName(e.target.value);
<input
    type="text"
    onChange={onNameChange}
>

基于类的组件获得一个setState函数,该函数接受部分状态更新并将它们合并到组件的状态中,但useState的setter不是这样工作的;它完全替换了状态项。这意味着,如果您使用的是问题中的复合状态项,则必须在每次调用setter时包含其所有部分

const [formObj, setFormObj] = useState({name: "", link: "", error: ""});
const onNameChange = ({target: {value}}) => setFormObj({...formObj, name: value}); // <== USUALLY WRONG

问题在于formObj中的数据可能已过时。您必须使用回调表单:

const [formObj, setFormObj] = useState({name: "", link: "", error: ""});
const onNameChange = ({target: {value}}) => setFormObj(formObj => ({...formObj, name: value}));

另一个问题是,如果在useffect/usemo//useCallback/等中使用这些状态中的任何一个作为依赖项,也很容易出错。

因此,是的,您的经理和技术架构师可能是正确的,使用单个useState调用(或useReducer)。但是,再一次,这是一种判断;您可以始终使用useFormObj的回调形式。

另一个选择:

您可以为表单对象创建一个钩子,其中包含接受字符串或事件的名称、链接和错误的单独设置器,如下所示:

// A reusable utility function: if the argument is an object with a `target` property,
// return `x.target.value`; otherwise, just return `x`.
function unwrapValue(x) {
    if (typeof x === "object" && x.target) {
        x = x.target.value;
    }
    return x;
}

// Off-the-cuff, untested, just a sketch
function useFormObj(initialFormObj = {name: "", link: "", error: ""}) {
    const [formObj, setFormObj] = useState(initialFormObj);
    const setters = useRef(null);
    if (!setters.current) {
        // Only true on first call
        setters.current = {
            setName(name) {
                name = unwrapValue(name);
                setFormObj(formObj => ({...formObj, name}));
            },
            setLink(link) {
                link = unwrapValue(link);
                setFormObj(formObj => ({...formObj, link}));
            },
            setError(error) {
                error = unwrapValue(error);
                setFormObj(formObj => ({...formObj, error}));
            },
        };
    }
    return [formObj, setters.current];
}

然后:

const [formObj, {setName, setLink, setError}] = useFormObj();
<input
    type="text"
    onChange={setName}
>

当您需要在多个组件中使用表单对象,或者您只想拥有更小的更容易测试的组件时,这很方便。

从留档(您必须稍微向下滚动到注释:):

注意事项

与类组件中的setState方法不同,useState不会自动合并更新对象。您可以通过将函数updater表单与对象传播语法相结合来复制此行为:

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

另一个选项是useReducer,它更适合于管理包含多个子值的状态对象。

 类似资料:
  • 我的程序使用Java脚本API,可以同时评估一些脚本。它们不使用共享脚本对象、绑定或上下文,但可以使用相同的和对象。我发现Java8中的OracleNashorn实现不是多线程的,返回,文档中说: 引擎实现不是线程安全的,不能用于在多个线程上并发执行脚本。 这是否意味着我应该为每个线程创建一个单独的实例?此外,文档中没有提到并发使用,而是: 每个已编译脚本都与一个脚本引擎相关联 可以假设线程安全依

  • 我是否需要为我的复杂Web应用程序使用单独的Docker容器,或者我可以将所有必需的服务放在一个容器中?有人能解释一下为什么我应该将我的应用程序划分为许多容器(例如容器,容器,容器)当我能够在一个容器中安装和启动所有东西时?

  • 问题内容: Facelets使用 jsfc* 属性将HTML元素转换为其关联的 JSF 组件。这对于快速原型制作非常有用,因为它允许您使用视觉设计工具创建视图。但是,我最近发现了 Cay Horstmann的 这篇 博客文章,他浪费了 jsfc 以及 h:dataTable 等复杂组件的使用。 __ *** 这让我感到震惊,因为 Cay Horstmann 是我最喜欢的Java书籍的多本作者。但是

  • 问题内容: 我打算使用Kubernetes和Ingress进行负载平衡。我正在尝试学习如何设置Flask,uWSGI和Nginx。我看到本教程将所有三个安装在同一容器中,我想知道是否应该使用它。 https://ianlondon.github.io/blog/deploy-flask-docker- nginx/ 我猜测将它们作为单独的容器和单独的容器的好处是它们可以分别进行单独缩放? 但是,F

  • 我无意中发现了一种非常普遍的做法。我甚至发现了一个网页,给它起了个名字,但我忘了名字,在谷歌上再也找不到那个网页了。 实践是,来自REST服务的每个JSON响应都应该具有以下结构: 或在错误情况下: 我的问题:JSON中为什么需要这样一个“status”属性?在我看来,这就是HTTP状态码的用途。 REST使用客户端和服务器之间的HTTP通信方式,例如,删除应该使用“DELETE”动词。同理,一个

  • 问题内容: 添加索引的开销有据可查,但是我无法找到有关何时使用各种索引的各种信息,这些信息与要建立索引的各种文档类型有关。 这是一个通用示例来说明问题: 说我们有以下实体 产品(名称,ProductID,ProductCategoryID,商店列表) 产品类别(名称,ProductCategoryID) 商店(名称,商店ID) 我是否应该将这三种不同类型的文档转储到一个索引中,每个索引都具有适当的