Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
377 views
in Technique[技术] by (71.8m points)

javascript - React(mobx) observer components render() not called when observable changes

In the following sample code, unfinishedTodoCount (under LoadContent component) is not updated when todo items checked.

I dereference unfinishedTodoCount in the render method of TodoListView so i think it must be tracked by mobx.

(I use "trigger rendering" button to force render() to update unfinishedTodoCount value.)

Question: So, why does not mobx trigger render() when unfinishedTodoCount changes?

Consideration: I am wondering if props.children() is running asynchronously so mobx cannot catch dereferencing.

(Solutions: Various solutions could be applied by uncommenting lines in the code.)

// 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>
        );
      }
    }

Complete source code here; https://codesandbox.io/s/simple-mobx-todolist-forked-hep3t?file=/index.js


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I think components render() is being called asynchronously (yet components are rendered in sync. as expected) per my proof of concept below so mobx can not track dereferencing in component.

Proof of Concept: Components are called async.

I added a console.log() call after LoadContent and its called before the console.log() in LoadContent.

<LoadContent>
{() => (
  <div>Tasks left: {this.props.todoList.unfinishedTodoCount}</div>
)}
</LoadContent>
{(()=>{console.log("after load content")})()}
rendering TodoListView 
*after load content* 
rendering LoadContent

(Complete source code here; https://codesandbox.io/s/simple-mobx-todolist-forked-hep3t?file=/index.js)

SOLUTION: We could use @observer decorator for child components or access those observable variables earlier in render() of parent components.

Actually, caveat of passing renderable callbacks to components is also stated in following documentation;

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...