acdlite / react-rx-component

Yet another RxJS library for React :) Create container components (also known as smart components) by transforming a sequence of props
82 stars 4 forks source link

"setState" on componentDidMount #4

Open efflam opened 9 years ago

efflam commented 9 years ago

Hi I'm trying to create a smart component to retrieve window size and pass it as props to the dumb component. It works fine when I resize the window. I was wondering how i could update width and height on componentDidMount with this approach.

const TestContainer = createRxComponent(props$ => {

  const resize$ = Observable
    .fromEvent(window, 'resize')
    .map( e => ({ width: window.innerWidth, height: window.innerHeight }) )
    .startWith({ width: 0, height: 0 })

  return Observable.combineLatest(props$, resize$, (props, { width, height }) => ({
    ...props,
    width,
    height
  }))

}, Test)

function Test(props){
  const { width, height } = props
  return (
    <div>
      <p>width: {width}</p>
      <p>height: {height}</p>
    </div>
  )
} 
acdlite commented 9 years ago

props$ receives only a single value before mounting, from inside the constructor. After than it updates on componentWillReceiveProps(), which is only called after mounting.

$componentDidMount = $props
  .skip(1)
  .take(1);

// Or just
$propsAfterMount = $props.skip(1);

This is a good topic for the docs, thanks!

istarkov commented 9 years ago

@acdlite But this $props.skip(1).take(1) is not componentDidMount, it's first componentWillReceiveProps call, which can never occurs.

There are situations (for example to detect size of component) where real componentDidMount is needed.

efflam commented 9 years ago

Thx @acdlite. I tried your suggestion with no success. TestContainer doesn't receive any props from his parent so componentWillReceiveProps is never called.

acdlite commented 9 years ago

Ah I see what you mean. @istarkov @Efflam Could you use a callback ref with a funcSubject() instead?

<div ref={() => componentDidMount$() }>
acdlite commented 9 years ago

@Efflam So your example would look like:

const TestContainer = createRxComponent(props$ => {
 const containerRef$ = funcSubject()
 const resize$ = Observable
    .fromEvent(window, 'resize');

 const size$ = Observable.combineLatest(containerRef$, resize$,
    containerRef => React.findDOMNode(component))
    .map( el => ({ width: el.innerWidth, height: el.innerHeight }) )
    .startWith({ width: 0, height: 0 })

  return Observable.combineLatest(props$, size$, $containerRef, (props, { width, height }, containerRef) => ({
    ...props,
    containerRef
    width,
    height
  }))

}, Test)

function Test(props){
  const { width, height, containerRef } = props
  return (
    <div ref={containerRef}>
      <p>width: {width}</p>
      <p>height: {height}</p>
    </div>
  )
}

EDIT: fixed (I believe)

acdlite commented 9 years ago

^ Btw I don't think that will work until https://github.com/acdlite/react-rx-component/issues/5 is fixed. I'll look at it today and write a test case to confirm.

miketembo commented 8 years ago

so whats the best way to do it