React(mobx) 观察者组件 render() 在可观察的变化时不被调用
React(mobx) observer components render() not called when observable changes
在下面的示例代码中,unfinishedTodoCount
(在 LoadContent
组件下)在选中待办事项时不会更新。
我在 TodoListView 的渲染方法中取消引用 unfinishedTodoCount
所以我认为它必须被 mobx 跟踪。
(我使用“触发渲染”按钮强制 render() 更新 unfinishedTodoCount
值。)
问题:那么,为什么当unfinishedTodoCount
变化时mobx不触发render()
?
考虑:我想知道 props.children()
是否是 运行 异步的,所以 mobx 无法捕获取消引用。
(解决方案:可以通过取消注释代码中的行来应用各种解决方案。)
// Uncomment following so render() will be called when unfinishedTodoCount changes.
//@observer
class LoadContent extends React.Component {
render() {
console.log("rendering LoadContent");
return (
<div>
{this.props.children({
// ...this.props,
})}
</div>
);
}
}
@observer
class TodoListView extends React.Component {
constructor(props) {
super(props);
this.state = {
triggerRender: false
};
}
render() {
console.log("rendering TodoListView");
// Uncomment following so render() will be called when unfinishedTodoCount changes.
//let todoCount = this.props.todoList.unfinishedTodoCount;
//console.log(todoCount);
return (
<div>
<input
type="Button"
onClick={() =>
this.setState({ triggerRender: !this.state.triggerRender })
}
value="Trigger rendering"
/>
<ul>
{this.props.todoList.todos.map((todo) => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
<div>
{/* Uncomment following so render() will be called when unfinishedTodoCount changes. */
/* {(() => (
<div>Tasks left: {this.props.todoList.unfinishedTodoCount}</div>
))()} */}
<LoadContent>
{() => (
<div>Tasks left: {this.props.todoList.unfinishedTodoCount}</div>
)}
</LoadContent>
</div>
</div>
);
}
}
完整的源代码在这里;
https://codesandbox.io/s/simple-mobx-todolist-forked-hep3t?file=/index.js
我认为根据下面的 我的概念证明 ,组件 render()
被异步调用(但组件按预期同步呈现),因此 mobx 无法跟踪取消引用在组件中。
概念验证:组件称为异步。
我在 LoadContent 之后添加了一个 console.log() 调用,它在 LoadContent 中的 console.log() 之前调用。
<LoadContent>
{() => (
<div>Tasks left: {this.props.todoList.unfinishedTodoCount}</div>
)}
</LoadContent>
{(()=>{console.log("after load content")})()}
rendering TodoListView
*after load content*
rendering LoadContent
(完整的源代码在这里;https://codesandbox.io/s/simple-mobx-todolist-forked-hep3t?file=/index.js)
解决方案: 我们可以对子组件使用@observer 装饰器,或者在父组件的 render()
中访问那些较早的可观察变量。
实际上,passing renderable callbacks to components
的注意事项也在以下文档中说明;
The notable caveat here is passing renderable callbacks to React
components, take for example the following example:
const MyComponent = observer(({ message }) =>
<SomeContainer
title = {() => {message.title}}
/> )
message.title = "Bar" At first glance everything might seem ok here,
except that the is actually not rendered by MyComponent (which
has a tracked rendering), but by SomeContainer. So to make sure that
the title of SomeContainer correctly reacts to a new message.title,
SomeContainer should be an observer as well. If SomeContainer comes
from an external lib, you can also fix this by wrapping the div in its
own stateless observer based component, and instantiating that one in
the callback:
const MyComponent = observer(({ message }) =>
<SomeContainer
title = {() => }
/> )
const TitleRenderer = observer(({ message }) =>
{message.title}} )
message.title = "Bar"
https://doc.ebichu.cc/mobx/best/react.html
在下面的示例代码中,unfinishedTodoCount
(在 LoadContent
组件下)在选中待办事项时不会更新。
我在 TodoListView 的渲染方法中取消引用 unfinishedTodoCount
所以我认为它必须被 mobx 跟踪。
(我使用“触发渲染”按钮强制 render() 更新 unfinishedTodoCount
值。)
问题:那么,为什么当unfinishedTodoCount
变化时mobx不触发render()
?
考虑:我想知道 props.children()
是否是 运行 异步的,所以 mobx 无法捕获取消引用。
(解决方案:可以通过取消注释代码中的行来应用各种解决方案。)
// Uncomment following so render() will be called when unfinishedTodoCount changes.
//@observer
class LoadContent extends React.Component {
render() {
console.log("rendering LoadContent");
return (
<div>
{this.props.children({
// ...this.props,
})}
</div>
);
}
}
@observer
class TodoListView extends React.Component {
constructor(props) {
super(props);
this.state = {
triggerRender: false
};
}
render() {
console.log("rendering TodoListView");
// Uncomment following so render() will be called when unfinishedTodoCount changes.
//let todoCount = this.props.todoList.unfinishedTodoCount;
//console.log(todoCount);
return (
<div>
<input
type="Button"
onClick={() =>
this.setState({ triggerRender: !this.state.triggerRender })
}
value="Trigger rendering"
/>
<ul>
{this.props.todoList.todos.map((todo) => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
<div>
{/* Uncomment following so render() will be called when unfinishedTodoCount changes. */
/* {(() => (
<div>Tasks left: {this.props.todoList.unfinishedTodoCount}</div>
))()} */}
<LoadContent>
{() => (
<div>Tasks left: {this.props.todoList.unfinishedTodoCount}</div>
)}
</LoadContent>
</div>
</div>
);
}
}
完整的源代码在这里; https://codesandbox.io/s/simple-mobx-todolist-forked-hep3t?file=/index.js
我认为根据下面的 我的概念证明 ,组件 render()
被异步调用(但组件按预期同步呈现),因此 mobx 无法跟踪取消引用在组件中。
概念验证:组件称为异步。
我在 LoadContent 之后添加了一个 console.log() 调用,它在 LoadContent 中的 console.log() 之前调用。
<LoadContent>
{() => (
<div>Tasks left: {this.props.todoList.unfinishedTodoCount}</div>
)}
</LoadContent>
{(()=>{console.log("after load content")})()}
rendering TodoListView
*after load content*
rendering LoadContent
(完整的源代码在这里;https://codesandbox.io/s/simple-mobx-todolist-forked-hep3t?file=/index.js)
解决方案: 我们可以对子组件使用@observer 装饰器,或者在父组件的 render()
中访问那些较早的可观察变量。
实际上,passing renderable callbacks to components
的注意事项也在以下文档中说明;
The notable caveat here is passing renderable callbacks to React components, take for example the following example:
const MyComponent = observer(({ message }) => <SomeContainer title = {() => {message.title}} /> )
message.title = "Bar" At first glance everything might seem ok here, except that the is actually not rendered by MyComponent (which has a tracked rendering), but by SomeContainer. So to make sure that the title of SomeContainer correctly reacts to a new message.title, SomeContainer should be an observer as well. If SomeContainer comes from an external lib, you can also fix this by wrapping the div in its own stateless observer based component, and instantiating that one in the callback:
const MyComponent = observer(({ message }) => <SomeContainer title = {() => } /> )
const TitleRenderer = observer(({ message }) => {message.title}} )
message.title = "Bar" https://doc.ebichu.cc/mobx/best/react.html