Search code examples
javascriptreactjsjsxmobxmobx-state-tree

Return number of "false" Boolean values from an array - MobX & React


I have a mock data file that has the content arranged like so:

{
  id: 6,
  name: 'example',
  time: new Date(),
  viewed: false,
},

My goal is to be able to count the number of "false" values, and return that count to a Component to be used as content.

I have a file that defines a ModelStore. The beginning of it is like so:

const Model = types
  .model('Model', {
    id: types.identifierNumber,
    name: types.string,
    time: types.Date,
    viewed: types.boolean,
  });

/** @typedef {typeof Model} Model */
/** @typedef {import('mobx-state-tree').Instance<typeof Model>} ModelInstance */

const ModelStoreData = types.model({ Models: types.array(Model) });

You can see the mock data is imported as an array.

I then came up with something like the following:

const ModelStore = types
  .compose(
    ModelStoreData,
  )
  .named('ModelStore')
  .views((self) => {
    /**
     * Count number of unviewed models
     * @returns {typeof self.Models}
     */
    const countUnviewed = (Models) => {
      /**
       * Numerical urgency levels for sorting
       * @enum {number}
       */
      const viewedValues = {
        [true]: 0,
        [false]: 1,
      };

      /** Returns a numerical value */
      const value = Model => viewedValues[Model.viewed];

      return Models
        // do something with value here to add up
    };

    return {
      get unviewedCount() {
        return countUnviewed(self.Models);
      },
    };
  });

/** @typedef {typeof ModelStore} ModelStore */
/** @typedef {import('mobx-state-tree').Instance<typeof ModelStore>} ModelStoreInstance */

export default ModelStore;

but I'm unsure how to proceed from here on how to actually count up the number of "false" values.

I then have this Component that needs that counted value to be inserted:

const myComponent = withRouter(({ history }) => {
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);

  const toggle = () => {
    setIsDropdownOpen(prevDropdownOpen => !prevDropdownOpen);
  };

  const handleOnClick = () => {
    history.push(routes.home.path);
  };

  return (
    <Dropdown isOpen={isDropdownOpen} toggle={toggle}>
      <DropdownToggle caret>
        Dropdown
      </DropdownToggle>
      <DropdownMenu right>
        <DropdownItem header>viewed items: 2</DropdownItem>
        <Button outline onClick={handleOnClick}>go home</Button>
      </DropdownMenu>
    </Dropdown>
  );
});

where "viewed items: 2" is located, substituting the "2" for the counted value.

Any guidance as to how to approach the situation would be appreciated.


Solution

  • You could use the array method filter to create a new array with every model that has viewed set to false and return the length of that array as unviewedCount.

    const ModelStore = types
      .compose(ModelStoreData)
      .named("ModelStore")
      .views(self => {
        return {
          get unviewedCount() {
            return self.Models.filter(model => !model.viewed).length;
          }
        };
      });
    

    You would then need to create an instance of the ModelStore and use the observer function on your component so that your component will re-render if unviewedCount changes.

    const store = ModelStore.create();
    
    const myComponent = withRouter(
      observer(({ history }) => {
        const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
    
        const toggle = () => {
          setIsDropdownOpen(prevDropdownOpen => !prevDropdownOpen);
        };
    
        const handleOnClick = () => {
          history.push(routes.home.path);
        };
    
        return (
          <Dropdown isOpen={isDropdownOpen} toggle={toggle}>
            <DropdownToggle caret>Dropdown</DropdownToggle>
            <DropdownMenu right>
              <DropdownItem header>
                viewed items: {store.unviewedCount}
              </DropdownItem>
              <Button outline onClick={handleOnClick}>
                go home
              </Button>
            </DropdownMenu>
          </Dropdown>
        );
      })
    );