1

What is the canonical React+Flux way to ID components that trigger changes to state?

I've got an application that allows a user to create palettes using the HSL color space.

Here's the component structure of my app:

Container (this component gets state and passes it down the chain)
 | PalettePicker
   | ColorPicker
     | Slider (this component fires action)
     | Slider
     | Slider
   | (As many ColorPickers as colors)
 | ImageSamples (not relevant, but dependent on palette)

Here's a look at the ColorPicker component:

ColorPicker component

Each ColorPicker contains 3 Slider components which trigger events that update the store. The store then updates the palette and passes the entire palette down to the Container component, which passes it down as props to its children components.

Here's my function that handles a slider change event in my Store (I'm using Reflux):

sliderChange: function(newValue) {
    var modifiedPalette = this.palette;
    modifiedPalette[newValue.colorIndex][newValue.colorPartsIndex] = newValue.value;
    this.trigger(modifiedPalette)
}

My palette is an array of HSL color values, so something like:

[ [350, 100, 50], [340, 100, 40], ... ]

A "color" is one of the 3-item arrays above and I'm calling each item in the color array a "color part" since it represents either the H, S, or L of the color.

Passing the color and color part index down as a prop seems inelegant. I'm currently building components like this:

colorPickers = palette.map(function(color, i) {
    return (
        <ColorPicker 
             key={i}
             colorIndex={i}
             color={color}
        />
    )
});

As far as I can tell, I need to pass colorIndex as a prop so that my child component can know which color in the palette it maps to so that I can pass that knowledge on to the store.

What's the idiomatic React+Flux way to do this?

Fabian N.
  • 3,807
  • 2
  • 23
  • 46
Matt Parrilla
  • 3,171
  • 6
  • 35
  • 54
  • Using colorIndex is good. Shouldn't color={this.props.palette[i]} be color={color} though. – J. Mark Stevens Sep 25 '15 at 20:05
  • Thanks, should be {color} – Matt Parrilla Sep 26 '15 at 01:15
  • @MattParrilla that depends on how many colors you can modify at once. If the only one - than you can keep `currentColor` property at your store and aviod passing color index prop. But if you can modify multiple collors - than you have one-2-many store-view relationship, and I don't see a better way. Actually, why do you think it's inelegant? – VB_ Sep 26 '15 at 16:50
  • `key={i} colorIndex={i}` feels clunky since I'm setting 2 props to the same value – Matt Parrilla Sep 28 '15 at 12:20

1 Answers1

1

I would suggest not letting the ColorPicker component call any action itself, but pass it as an "onChange" prop. It doesn't need to know what index it is. You can bind the onChange function so that it will pass the index:

colorPickers = palette.map(function(color, i) { 
   return ( 
      <ColorPicker key={i} 
          onChange={this.changeColor.bind(this, i)} 
          color={color} 
      /> 
   ) 
});

The changeColor function looks like this:

changeColor : function(colorIndex,  colorArray) {...}

It should receive the whole color (the array) and fire the appropriate Reflux action with it and the index. That way the ColorPicker only have to fire the onChange function with the new color that changed.

Do the same trick for every slider inside the color picker. Pass it an onChange function that is already bounded to its partIndex. When that function is triggered, The ColorPicker should construct a new color array, and call its own onChange prop.

Hope it was clear enough. The motivation is - every component should get a simple callback function and output only what it is responsible for. It doesn't need to know any other details. Instead of passing more and more useless index props all the way down, just pass bounded callbacks. It will simplify your components and will let your high-level component deal with the details of constructing the Flux action itself.

Matt Parrilla
  • 3,171
  • 6
  • 35
  • 54
lyosef
  • 6,092
  • 2
  • 27
  • 20
  • This was similar to my original implementation but I had some coworkers point out that using callbacks wasn't pure "one-way data flow." The question then is: are we supposed to be absolutists when it comes to direction of data flow? If not, where is the line? – Matt Parrilla Sep 28 '15 at 12:08
  • Interestingly, some evidence in favor of callbacks from the Facebook tutorial: https://facebook.github.io/react/docs/tutorial.html#callbacks-as-props – Matt Parrilla Sep 28 '15 at 12:18
  • 1
    I think that passing callbacks down still demonstrates a "one direction data-flow" thinking. The whole idea of "data down, actions up" with Flux is that components shouldn't change other components directly, but instead render a model, and possibly fire actions that may change that model. IMO a high level component that passes down callbacks, but fire an action itself (and only then recieves its new data), is still a solid implementation of that principle. – lyosef Sep 30 '15 at 00:08