React 组件不重新渲染,尽管 props 中的 Object 发生了变化
React component not re-rendering, although Object in props changes
我知道,有很多很多类似的问题.. **重复报警!**
但是: 我保证,我浏览了所有这些。我现在很确定,这是另一种情况,可能与道具是一个对象有关(根据我在这里阅读的内容)。但无论如何我无法解决以下问题:
class CsvListDropdown extends Component {
constructor(props) {
super(props);
this.state = { sessions: props.sessions }
this csvsInSession = this.csvsInSession.bind(this);
}
csvsInSession(sessions) {
return (sessions
.map(keys => Object.entries(keys)[2][1])
.map((csv, i) => (
<option value={csv} key={i}>{csv}</option>
))
)
}
render() {
const { isLoading } = this.props
if (isLoading) { blablabla.. }
else {
return (
...
<select value={this.props.sessions[0].currentCsv}>
{this.csvsInSession(this.state.sessions)}
</select>
...
)
}
}
}
export default withTracker(() => {
const handle = Meteor.subscribe('sessions');
return {
isLoading: !handle.ready(),
sessions: Sessions.find({}).fetch()
};
})(CsvListDropdown);
现在,我正在从客户端向 Sessions 集合写入另一个文档,其中包含 .csv 文件名,同时这个新的 csv 文件正在上传到远程服务器。 console.log(this.props.sessions)
给了我一个最新的数组。但是组件本身不会重新渲染。
我还不明白的是:console.log(this.state.sessions)
returns undefined
。 (注:状态)
到目前为止我尝试了什么:
{this.csvsInSession(this.props.sessions)}
(注:props)
将 withTracker
/ State
/ Props
添加到父组件并从 state
或传递 sessions
对象props
作为子组件的参数,应该重新渲染。
forceUpdate()
- componentWillUpdate()
同样重要的是:该组件应该大约在同一时间重新呈现另一个组件也重新呈现(显示上传的 CSV 的内容,return 来自微服务并写入另一个集合)。后者确实会重新呈现。但是那个下拉菜单不会。啊!
this.state
只会在您调用 this.setState()
时发生变化,而您并未这样做。您正在使用 props
中的值初始化 state
,但仅在首次实例化组件时在构造函数中进行初始化。在那之后,即使 props
更改您的组件可能会重新呈现,但它显示的内容不会更改,因为 state
尚未更新。
事实上,似乎没有任何理由将数据存储在该组件的状态中。它也可能是一个功能性的展示组件:
function CsvListDropdown(props) {
function csvsInSession(sessions) {
return (sessions
.map(keys => Object.entries(keys)[2][1])
.map((csv, i) => (
<option value={csv} key={i}>{csv}</option>
))
)
}
const { isLoading } = props;
if (isLoading) { blablabla.. }
else {
return (
...
<select>
{csvsInSession(props.sessions)}
<select>
...
)
}
}
通常,您的所有组件都应该 stateless functional components,除非它们出于某种原因特别需要存储内部状态。
现在终于解决了,原来组件did其实是随时更新的,只是我没有通知它,仅仅是因为数组中的最新项目被悄悄地附加到下拉列表的 bottom。然而,这是我没有预料到的,因为我 发布了 具有降序 排序 .
的集合
// server-side
Meteor.publish('sessions', function() {
return Sessions.find({ userId: this.userId }, { sort: {createdAt: -1} });
});
服务器端似乎是排序的错误位置。它根本没有效果。所以在客户端排序,订阅时:
// client-side
export default withTracker(() => {
const handle = Meteor.subscribe('sessions');
return {
isLoading: !handle.ready(),
sessions: Sessions.find({}, { sort: {createdAt: -1} }).fetch()
};
})(App)
我在问题中遗漏了一个重要细节,那就是我如何设置下拉字段的 value:
<select value={this.props.sessions[0].currentCsv}>
{this.csvsInSession(sessions)}
</select>
吸取了教训:如果您认为您的 React 组件没有重新渲染,请务必在假设是这样之前检查它是否正确。
作为调试的副作用,我重构了我的组件。现在 Meteor.subscribe()
在父组件中,它包含所有必须处理 sessions
对象的子组件。 sessions
对象作为道具从父级传递给(孙)子级。我认为这样更易读,也更容易维护。
我知道,有很多很多类似的问题.. **重复报警!**
但是: 我保证,我浏览了所有这些。我现在很确定,这是另一种情况,可能与道具是一个对象有关(根据我在这里阅读的内容)。但无论如何我无法解决以下问题:
class CsvListDropdown extends Component {
constructor(props) {
super(props);
this.state = { sessions: props.sessions }
this csvsInSession = this.csvsInSession.bind(this);
}
csvsInSession(sessions) {
return (sessions
.map(keys => Object.entries(keys)[2][1])
.map((csv, i) => (
<option value={csv} key={i}>{csv}</option>
))
)
}
render() {
const { isLoading } = this.props
if (isLoading) { blablabla.. }
else {
return (
...
<select value={this.props.sessions[0].currentCsv}>
{this.csvsInSession(this.state.sessions)}
</select>
...
)
}
}
}
export default withTracker(() => {
const handle = Meteor.subscribe('sessions');
return {
isLoading: !handle.ready(),
sessions: Sessions.find({}).fetch()
};
})(CsvListDropdown);
现在,我正在从客户端向 Sessions 集合写入另一个文档,其中包含 .csv 文件名,同时这个新的 csv 文件正在上传到远程服务器。 console.log(this.props.sessions)
给了我一个最新的数组。但是组件本身不会重新渲染。
我还不明白的是:console.log(this.state.sessions)
returns undefined
。 (注:状态)
到目前为止我尝试了什么:
{this.csvsInSession(this.props.sessions)}
(注:props)将
withTracker
/State
/Props
添加到父组件并从state
或传递sessions
对象props
作为子组件的参数,应该重新渲染。forceUpdate()
- componentWillUpdate()
同样重要的是:该组件应该大约在同一时间重新呈现另一个组件也重新呈现(显示上传的 CSV 的内容,return 来自微服务并写入另一个集合)。后者确实会重新呈现。但是那个下拉菜单不会。啊!
this.state
只会在您调用 this.setState()
时发生变化,而您并未这样做。您正在使用 props
中的值初始化 state
,但仅在首次实例化组件时在构造函数中进行初始化。在那之后,即使 props
更改您的组件可能会重新呈现,但它显示的内容不会更改,因为 state
尚未更新。
事实上,似乎没有任何理由将数据存储在该组件的状态中。它也可能是一个功能性的展示组件:
function CsvListDropdown(props) {
function csvsInSession(sessions) {
return (sessions
.map(keys => Object.entries(keys)[2][1])
.map((csv, i) => (
<option value={csv} key={i}>{csv}</option>
))
)
}
const { isLoading } = props;
if (isLoading) { blablabla.. }
else {
return (
...
<select>
{csvsInSession(props.sessions)}
<select>
...
)
}
}
通常,您的所有组件都应该 stateless functional components,除非它们出于某种原因特别需要存储内部状态。
现在终于解决了,原来组件did其实是随时更新的,只是我没有通知它,仅仅是因为数组中的最新项目被悄悄地附加到下拉列表的 bottom。然而,这是我没有预料到的,因为我 发布了 具有降序 排序 .
的集合// server-side
Meteor.publish('sessions', function() {
return Sessions.find({ userId: this.userId }, { sort: {createdAt: -1} });
});
服务器端似乎是排序的错误位置。它根本没有效果。所以在客户端排序,订阅时:
// client-side
export default withTracker(() => {
const handle = Meteor.subscribe('sessions');
return {
isLoading: !handle.ready(),
sessions: Sessions.find({}, { sort: {createdAt: -1} }).fetch()
};
})(App)
我在问题中遗漏了一个重要细节,那就是我如何设置下拉字段的 value:
<select value={this.props.sessions[0].currentCsv}>
{this.csvsInSession(sessions)}
</select>
吸取了教训:如果您认为您的 React 组件没有重新渲染,请务必在假设是这样之前检查它是否正确。
作为调试的副作用,我重构了我的组件。现在 Meteor.subscribe()
在父组件中,它包含所有必须处理 sessions
对象的子组件。 sessions
对象作为道具从父级传递给(孙)子级。我认为这样更易读,也更容易维护。