简体   繁体   中英

React/MobX component not re-rendering after state change (found a weird solution, would like to know why)

I have a React.js app with MobX for state management. Here is the ItemStore class:

class ItemStore {
    @observable items: IItem[] = []

    @action loadItems = () => {
        this.items = [
            {
                name: 'BeyBlade',
                category: 'Toy',
                amount: 5
            },
            {
                name: 'Legos',
                category: 'Toy',
                amount: 10
            },
            {
                name: 'Coka Cola',
                category: 'Food',
                amount: 8
            },
            {
                name: 'Gummy Bears',
                category: 'Food',
                amount: 13
            }

        ]
    }
}

export default createContext(new ItemStore());

App.tsx:

function App() {

  const itemStore = useContext(ItemStore);
  const { loadItems } = itemStore;

  return (
    <Fragment>
      <Container>
        <Button content='Load Items' onClick={() => loadItems()} />
        <ItemList />
      </Container>
    </Fragment>
  );
}

export default observer(App);

When the button is pressed, supposedly 'loadItems' action runs and populates 'items' observable, and ItemList component re-renders to showcase the items. The problem occurs when I press the button. In the MobX dev tool you can clearly see the four items were loaded successfully, but ItemList doesn't re-render. However, if I add the 'items' observable in the deconstructed itemStore like this:

const { items, loadItems } = itemStore;

Boom, ItemList reloads with the items when the button is clicked!

But I know this shouldn't be the case, because when compiled it gives warning

Line 11:11: 'items' is assigned a value but never used

And also, in the tutorial source code I am referring to, the whole deconstructing is omitted there's nothing in App component that interacts with items directly.

I put observer in all components, so they can react to state changes. I really would like to know what's causing the ItemList to not re-render after a state change, and why explicitly bringing 'items' observable in the App.tsx fixed the issue, when in theory it shouldn't matter.

Thank you all, stay safe & happy!

EDIT-------------------------------

Here is ItemList.tsx also, thank you:

export const ItemList = () => {

    const itemStore = useContext(ItemStore);
    const {items} = itemStore;

    return (
        <Segment clearing>
            <Item.Group divided>
                {items.map(item => (
                    <Item key={item.name}>
                        <Item.Content>
                            <Item.Header>{item.name}</Item.Header>
                            <Item.Description>
                                Category: {item.category}, Amount: {item.amount}
                            </Item.Description>
                        </Item.Content>
                    </Item>
                ))}
            </Item.Group>
        </Segment>
    )
}

export default observer(ItemList);

For the price of concussion from two days of banging my head against the wall, I found why!

For a (functional) React component to be able to 'observe' and apply state changes to itself, it needs to be exported as a higher-order observer function that takes the component as the parameter:

export const RandomComponent = () => {
    return (
        <div>Hello world!</div>
    )
}

export default observer(RandomComponent);

In the example component above, I allowed a lenience for mistake by specifying that the not-observer RandomComponent can also be exported. When I export RandomComponent instead of observer(RandomComponent), I am exporting a version of the component that cannot 'observe' MobX state changes.

My mistake was leaving the component just like above and exporting { RandomComponent } instead of the default export.

I hope I am the only newbie making this mistake, but hope anyone find this useful:D

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM