Javascript NextJS-can';使用浅层路由器推送时,无法识别页面组件上发生状态更改的位置
我有一个应用程序。它使用NextJS。我有一个页面,如下所示:Javascript NextJS-can';使用浅层路由器推送时,无法识别页面组件上发生状态更改的位置,javascript,reactjs,next.js,Javascript,Reactjs,Next.js,我有一个应用程序。它使用NextJS。我有一个页面,如下所示: import React from 'react' import { parseQuery } from '../lib/searchQuery' import Search from '../components/search' class SearchPage extends React.Component { static getInitialProps ({ query, ...rest }) { consol
import React from 'react'
import { parseQuery } from '../lib/searchQuery'
import Search from '../components/search'
class SearchPage extends React.Component {
static getInitialProps ({ query, ...rest }) {
console.log('GET INITIAL PROPS')
const parsedQuery = parseQuery(query)
return { parsedQuery }
}
constructor (props) {
console.log('CONSTRUCTOR OF PAGE CALLED')
super(props)
this.state = props.parsedQuery
}
render () {
return (
<div>
<div>
<h1>Search Results</h1>
</div>
<div>
<h1>DEBUG</h1>
<h2>PROPS</h2>
{JSON.stringify(this.props)}
<h2>STATE</h2>
{JSON.stringify(this.state)}
</div>
<div>
<Search query={this.state} />
</div>
</div>
)
}
}
export default SearchPage
这里的意图是CSR接管-因此是浅层的路由器.push
。页面URL发生更改,但不应再次触发getInitialProps
,随后的查询更改将通过componentWillUpdate
等进行处理。。我确认getInitialProps
由于缺少相应的console.log
触发,因此不会再次触发
问题
但是,在选中/取消选中Search
组件上的select字段时,我惊讶地发现SearchPage
的状态仍在更新,尽管没有证据表明调用了this.setState()
constructor
没有被调用,getInitialProps
也没有被调用,所以我不知道是什么导致状态发生变化
初始SSR后,调试块如下所示:
// PROPS
{
"parsedQuery": {
"manufacturer": [],
"lowPrice": "",
"highPrice": ""
}
}
// STATE
{
"manufacturer": [],
"lowPrice": "",
"highPrice": ""
}
然后在搜索
中检查选择字段后,它会更新为:
// PROPS
{
"parsedQuery": {
"manufacturer": ["Apple"],
"lowPrice": "",
"highPrice": ""
}
}
// STATE
{
"manufacturer": ["Apple"],
"lowPrice": "",
"highPrice": ""
}
我找不到对这种行为的解释,没有任何输出到控制台,我也找不到如何通过开发工具跟踪状态更改的来源
当然,只有通过componentdiddupdate
进行更新时,状态才应该更新?而且parsedQuery
prop不应该只由getInitialProps
更新吗?这就是创造和注入它的原因
为了进一步增加混淆,如果我更改搜索
上的数字输入
字段(例如低价
),URL会按预期更新,但调试块中的道具或页面状态会发生更改。我无法理解这种不一致的行为
这是怎么回事
编辑
我增加了回购协议。它将这个问题复制到GitHub上的MWE上,您可以在这里克隆它:哇,有趣的问题。这是一个有趣的小难题 TL;医生:这是你的错,但你是怎么做到的真的很微妙。首先,问题出在这一行: 在本例中,是这样的:
this.state = props.parsedQuery
让我们考虑一下那里到底发生了什么。
在IndexPage.getInitialProps中,您正在执行以下操作:`
const initialQuery = parseQuery({ ...query })
return { initialQuery }
通过Next的机制,这些数据将通过App.getInitialProps以pageProps.initialQuery的形式返回,然后在IndexPage中成为props.initialQuery,然后将其整体传递到搜索组件,搜索组件在其中“复制”对象以避免其变异。很好,对吗
你错过了什么
在lib/searchQuery.js中是这样一行:
searchQuery[field] = []
同一个数组正在向下传递到搜索中-除非您没有复制它。您正在复制props.query-它包含对该数组的引用
然后,在搜索组件中,更改复选框时执行以下操作:
const labelValue = this.state[label]
您正在修改在构造函数中“复制”的数组。你正在直接改变你的状态!这就是为什么initialQuery在主页上显示为更新-您对initialQuery引用的制造商数组进行了变异-它从未被复制。您拥有在getInitialProps中创建的原始引用
您应该知道的一件事是,即使在浅推时没有调用getInitialProps,应用程序组件仍然会重新呈现。它必须能够反映消耗组件的路线变化。在内存中对该数组进行变异时,重新渲染会反映更改。当您添加价格时,您没有改变initialQuery对象
解决这一切的办法很简单。在搜索组件构造函数中,需要查询的深度副本:
this.state = { ...cloneDeep(props.query) }
进行更改后,问题就会消失,您在打印输出中不再看到initialQuery的更改—正如您所期望的那样
您还需要更改此选项,即在您的状态下直接访问阵列:
const labelValue = this.state[label]
为此:
const labelValue = [...this.state[label]]
以便在更改阵列之前复制阵列。您可以通过立即调用setState来掩盖这个问题,但实际上您是在直接改变组件状态,这将导致各种奇怪的错误(如本例)
这是因为在组件状态中有一个全局数组发生了变异,所以所有这些变异都反映在不同的地方
const labelValue = [...this.state[label]]