Merging Immutable.js maps in flux store with internal state of React component

I have slowly converted my React + Flux apps to use Immutable.js data structures. I am using the original fake FWS implementation using vanilla.

One of the problems I ran into was the state of the mixing component with the state sourced from the Flux repositories .

I save all important business logic states in stores. But my rule was to maintain UI related state inside components. Stores don't need to worry if, for example, the drop-down menu is open or not, right?

The problem occurs when an action is performed on a component that changes state in the same component store. Let's say we have a component with a dropdown menu that is open. An item is selected in this dropdown menu. The action is propagated to ItemStore

, the store emits the change, and the component gets the new state from the store.

_onChange() {
  this.setState(this._getState());
}
_getState() {
  if(this.state === undefined) {
    return {
      data: Immutable.Map({
        selectedItem: ItemStore.getSelectedItem(),
        items: ItemStore.getItems(),
        menuIsOpen: false
      })
    };
  }
  return {
    data: this.state.data.merge(Immutable.Map({
      selectedItem: ItemStore.getSelectedItem(),
      items: ItemStore.getItems(),
      menuIsOpen: this.state.data.get("menuIsOpen")
    }))
  };
}

      

At the same time, in the component, clicking the dropdown menu triggers an old-fashioned event onClick

. I have a function _handleClick

that uses setState to close the dropdown menu (local state).

_handleClick(event) {
  event.preventDefault();
  this.setState({
    data: this.state.data.set("menuIsOpen", !this.state.data.get("menuIsOpen"))
  });
}

      

The problem is that it _handleClick

ends so quickly after _getState

that it doesn't have an updated copy this.state.data

. Therefore, the previously selected element is shown in the render

component method this.state.data.get("selectedItem")

.

When I do this with a POJO, React setState

seems to load everything correctly, so this has never been a problem. But I don't want to have state that is not part of Immutable.Map, because I want to use "pure" render

ing. However, I don't want to inject UI state into my stores because I feel like it might get messy real.

Is there a way to fix this? Or is it just bad practice for combining local Immutable.Map state and Immutable.Map state in one component?

RELEVANT: I'm not a fan of the pattern if(this.state === undefined)

to set an initial local state menuIsOpen

in my method _getState

. This could be a sign that I am trying to do something wrong.

+3


source to share


2 answers


You can pass a callback setState

to install the kernel update.

_onChange() {
  this.setState(state => this._getState(state));
}
_getState(state) {
  if(state === undefined) {
    return {
      data: Immutable.Map({
        selectedItem: ItemStore.getSelectedItem(),
        items: ItemStore.getItems(),
        menuIsOpen: false
      })
    };
  }
  return {
    data: state.data.merge(Immutable.Map({
      selectedItem: ItemStore.getSelectedItem(),
      items: ItemStore.getItems(),
      menuIsOpen: state.data.get("menuIsOpen")
    }))
  };
}

      



With regard to your related moment, you can take a look at getInitialState

.

+3


source


Why are there two separate actions when you press (shoot to save, close menu)? Instead, you can say that when they click on the menu item, we need to close the menu item and warn them to save the new value.



_handleClick(event) {
  event.preventDefault();
  this.setState({
    data: this.state.data.set("menuIsOpen", false)
  }, function() {
    alertTheSelectionChange(selectedItem)
  });
}

      

+2


source







All Articles