anthonyshort / deku

Render interfaces using pure functions and virtual DOM
https://github.com/anthonyshort/deku/tree/master/docs
3.42k stars 131 forks source link

Writing a component as a plain function, a la React 0.14 stateless functions #288

Closed Zacqary closed 8 years ago

Zacqary commented 8 years ago

I haven't had much time to experiment with Deku yet, so this question is admittedly coming from a place of ignorance. I'm wondering how easy it is to use React's stateless component syntax, which is basically just:

let MyComponent = props =>  <BunchOfJSX someProperty={props.something}/>

I like this syntax, I just wish it didn't come with the bloat of the rest of React. It would be great if I could just swap out React for Deku and have all my stateless components still work (since they don't use any React APIs, just props => <JSXElement>). But my understanding is that Deku components have to be objects which include a render function.

Is this purely functional syntax already supported in Deku and I'm just missing it?

anthonyshort commented 8 years ago

You can use a similar syntax. It's been in Deku for a lot longer than React :) It's not exactly the same though. We still pass the whole model in as normal, and you can still use updateState if you want.

let MyComponent = ({props}, updateState) =>  <BunchOfJSX someProperty={props.something}/>

The next version of Deku is removing state completely from components though. I use this syntax whenever I don't need lifecycle hooks (which is most of the time anyway).

Svjard commented 8 years ago

Can you explain the reasoning behind why state is being removed in the next release? Thanks.

anthonyshort commented 8 years ago

@Svjard State can pretty trivially be managed by something like Redux at the top level. Updating state is just an action in the UI like anything else. Components will end up like this:

Elm-style

function render (model, { dispatch }) {
  let click = () => dispatch('update_state', model.id, { clicked: true }) 
  return <button onClick={click}>{model.props.clicked ? 'clicked' : 'not clicked'}</button>
}

function update (state, action) {
   switch (action.type) {
     'UPDATE_STATE': 
         return { ...state, clicked: true } 
      }
   }
}

export {
  render,
  update
}

React style

function render (model, { state, dispatch }) {
  let state = state[model.id]
  let click = () => dispatch('update_state', model.id, { clicked: true }) 
  return <button onClick={click}>{state.clicked ? 'clicked' : 'not clicked'}</button>
}

export {
  render
}

Either way it's extremely easy to just push the state that Deku would normally store into the flow of the rest of your app. Components then have no local state, so they'll be really easy to test, since you just need to check for rendered virtual elements and actions firing (which are just plain objects).