threepointone / redux-react-local

local component state via redux
370 stars 21 forks source link

Let getInitial (and other methods?) take more than just props #8

Closed jtadmor closed 8 years ago

jtadmor commented 8 years ago

E.g.

    function getInitial(props){
      if (typeof initial !== 'function'){
        return initial;
      }
      return initial(props);
    }

//could be

   function getInitial({ props, context, state }){
      if (typeof initial !== 'function'){
        return initial;
      }
      return initial({ props, context, state });
    }

Here state would be the global redux app state. Without explicitly passing in the global state, you would have to do something like

@connect(  ({ state, props }) => [someProp]: state[someKey] )
@local( { ...args, initial: ({ props }) => f( props[someProp] ) )

Instead you could do

@local( { ...args, initial: ({ state }) => f( state[someKey] ) )`

Same with if you wanted to use some other values stored in the context.

I would be happy to try to submit a PR for this.

( I wrote it using params instead of ordered arguments because it makes it easier to future proof the code in case you want to pass in less or more or different arguments, and with just about no extra code) [edited for formatting - π]

threepointone commented 8 years ago

I'm not certain of this just yet. If I read data off context (all of context? some key of context?), then I'll have to ensure that I can listen to changes on this key as well, else it'll have stale idents in the react tree.

Reading off redux state also seems... 'global' to me; it implies that an ident can be a function of anything. Can you think of a use case where this/ context would be useful?

jtadmor commented 8 years ago

I had not thought about needing to make sure you have the right listeners on global state and how that would complicate things.

I do agree it's harder to think of a case where ident should depend on anything other than props.

The use case I was thinking about is basically just avoiding the above, where you need an @connect as well as an @local.

Even something simple like this:

@connect( ({ props, state }) => ({ itemIds: state.items }) )
function ListOfItems({ items }) {
  return (
    <ul>
      { itemIds.map( itemId => {
        const isOpen = itemsOnSuperSale.indexOf(itemId) > -1
        const item = items[itemId]
        return ( <Item item={item} isOpen={isOpen} /> )
      })}
    </ul>
  )
}

@local({ 
  ...args,
  initial: ({ props, state }) => ({
      item: state.entities[props.id],
      isOpen: state.itemsOnSuperSale.indexOf(props.id) > -1
    })
  } 
})
function Item({ item, isOpen, dispatch, $ }) {
  return (
    <div>
      <h4>{item.name}</h4>
      { isOpen && <span>{item.description}</span> }
      <button onClick={ => $('toggle', { open: !isOpen })}>Toggle Me!</button>
    </div>
  )
}

Can't think right now of a scenario where this is a must-have, so no worries if it's a hassle. (sorry about ugly code, not sure how to space it out). [edited - π]

threepointone commented 8 years ago

Personally, I feel like explicitly passing it props via connect is better, but I'll keep this issue open and think on it. Thank you!

threepointone commented 8 years ago

I'm pretty convinced that the ident for a component should only be a function of its props. Marking this closed, but let me know if you find examples that don't agree. Thank you!