问题描述
假设我有一个React应用,该应用具有内部状态,其中我存储了类似对象
const [car,setCar] = useState(new Car());
假设我的班级Car如下所示:
class Car {
constructor(brand) {
this.carname = brand;
}
present() {
return "I have a " + this.carname;
}
}
运行和调试应用程序时,我可以将Car对象保存到状态中,也可以检索它并调用present()。
现在,当我对present()函数进行更改时,例如
present() {
return "I have a " + this.carname + " and it is shiny";
}
然后由于快速刷新,我的应用程序被刷新。但不幸的是,由于该对象已经存储在状态中,因此它将无法接收功能实现的更新。
有没有一种方法可以更改代码,以便使用快速刷新功能也可以为处于React状态的对象更新函数实现?
我尝试通过原型更新方法,但是它也没有用。
解决方法
根据设计,“快速刷新”将尽可能保留状态。但是useEffect将始终在快速刷新期间更新,而不管依赖项数组中的内容如何。只要提供一个空的依赖关系数组,就可以在useEffect内使用setState来更改快速刷新之间的状态。
const [car,setCar] = useState(new Car());
useEffect(() => {
setCar(new Car());
},[])
请记住,useEffect仅在第一个渲染之后运行,因此您需要在useState挂钩参数中保留默认状态new Car()
,以防止发生任何未定义的错误。
这实际上有点棘手,因为您要实现的目标在某种程度上与react围绕不可变的设计背道而驰。使用def parameterize(search_string,kw):
expanded = search_string.split(maxsplit=14)
sql = ""
for word in expanded:
sql = "{sql}{cond_keyword} ? ".format(sql=sql,cond_keyword=kw if sql and len(expanded) > 1 else "",word=word
)
sql = "{sql}".format(sql=sql.lstrip().rstrip())
qargs = tuple(expanded)
return sql,qargs
def ka_search(search_str):
and_cond = parameterize(search_str,"AND")
near_cond = parameterize(search_str,"NEAR")
near_params = near_cond[0]
and_params = and_cond[0]
# build a final tuple of all parameters in order of the individual sub-queries that are union-ed together
args1 = list(and_cond[1])
args2 = [search_str]
args3 = list(near_cond[1])
args = args1 + args2 + args3 + args2
try:
cursor = db_connect()
cursor.execute("""
SELECT Title,CONCAT(RTRIM(LEFT(Bodytext,150)),'...') as BodyText,Keywords,RecId,rank,CONCAT('/kb/',RecId) AS Link
FROM KnowledgeArticle AS FT_TBL INNER JOIN
CONTAINSTABLE(KnowledgeArticle,(Title,keywords),{and_params}) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
UNION
SELECT Title,RecId) AS Link
FROM KnowledgeArticle AS FT_TBL INNER JOIN
FREETEXTTABLE(KnowledgeArticle,Title,?) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
UNION
SELECT Title,RecId) AS Link
FROM KnowledgeArticle AS FT_TBL INNER JOIN
CONTAINSTABLE(KnowledgeArticle,{near_params}) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
SELECT Title,Bodytext,?) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
ORDER BY rank DESC
""".format(and_params=and_params,near_params=near_params),args)
可以轻松地直接更改类方法并将其分配给现有实例。
这将替换原始汽车实例的原型并更改方法的实现。如您所说,这是行不通的,因为对对象进行突变不会触发重新渲染。
useState使用Object.setPrototypeOf(car,Car.prototype)
比较对象,在这种情况下,由于两个对象具有相同的引用,因此不会触发重新渲染。
为了Object.is()
,您需要将具有新原型和现有实例的确切属性的新对象传递到Object.is()
。使用基本扩展运算符setCar()
或{..car}
无法做到这一点,因为这些方法仅复制对象自身的可枚举属性。
您可以改为使用以下命令创建具有与新原型Object.assign({},car)
完全相同的属性的新对象。
可以将其放置在useEffect内,它将在每次更改原始Car类时运行
setCar(Object.assign(Object.create(Car.prototype),car))