Skip to content

Commit

Permalink
More robust state/props handling
Browse files Browse the repository at this point in the history
Allowing for container components to include props passed from the
parent component in conjunction with the store's state to obtain the
final props for the component.
  • Loading branch information
ethul committed Nov 17, 2016
1 parent 767a1ae commit 9eab8c7
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 25 deletions.
28 changes: 22 additions & 6 deletions src/React/Redux.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@ var Redux = require('redux');

var ReactRedux = require('react-redux');

var actionType = '@@PURESCRIPT_REACT_REDUX';

function startsWithActionType(actionForeign) {
var index = actionForeign.type.indexOf(actionType);

return index === 0;
}

function makeActionForeign(action) {
var constructorName = action.constructor && action.constructor.name ? action.constructor.name : 'UnknownConstructorName';

var actionForeign = {
type: '@@purescript',
type: actionType + '/' + constructorName,
action: action
};

Expand All @@ -16,9 +26,7 @@ function makeActionForeign(action) {
exports.createStore_ = function createStore_(reducer, state, enhancer){
return function(){
function reducerForeign(stateReducerForeign, actionForeign){
var action = actionForeign.action;

var result = action === undefined ? stateReducerForeign : reducer(action)(stateReducerForeign);
var result = startsWithActionType(actionForeign) ? reducer(actionForeign.action)(stateReducerForeign) : stateReducerForeign;

return result;
}
Expand All @@ -43,8 +51,16 @@ exports.createStore_ = function createStore_(reducer, state, enhancer){
};
};

exports.connect = function connect(stateToProps){
return ReactRedux.connect(stateToProps);
exports.connect_ = function connect_(Tuple, mapStateToProps, reactClass){
function mapStateToPropsForeign(state, props) {
var statePropsTuple = Tuple(state)(props);

var result = mapStateToProps(statePropsTuple);

return result;
}

return ReactRedux.connect(mapStateToPropsForeign)(reactClass);
};

exports.dispatch_ = function dispatch_(thisForeign, action){
Expand Down
56 changes: 37 additions & 19 deletions src/React/Redux.purs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module React.Redux
( ReduxReactClass
, ReduxReactClass'
, ReduxEffect
, REDUX
, Reducer
Expand All @@ -20,8 +21,10 @@ module React.Redux
, ComponentDidUpdate
, ComponentWillUnmount
, createClass
, createClass'
, createProviderElement
, createElement
, createElement'
, createStore
, createStore'
, reducerOptic
Expand All @@ -31,19 +34,22 @@ module React.Redux
, fromEnhancerForeign
) where

import Prelude (Unit, (>>=), (<$), const, pure, id, unit)
import Prelude (Unit, (>>=), (<<<), const, pure, id, unit)

import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class (class MonadEff, liftEff)

import Data.Either (Either, either)
import Data.Function.Uncurried (Fn2, Fn3, runFn2, runFn3)
import Data.Lens (Getter', Lens', Prism', matching, set, view)
import Data.Lens (Getter', Lens', Prism', matching, set, to, view)
import Data.Tuple (Tuple(..), fst)

import Unsafe.Coerce (unsafeCoerce)

import React as React

type ReduxReactClass' state props = ReduxReactClass state Unit props

type Reducer action state = action -> state -> state

type ReducerForeign action state = Fn2 action state state
Expand Down Expand Up @@ -106,10 +112,13 @@ spec getInitialState render =
spec' :: forall props eff f action. Render props Unit eff f action -> Spec props Unit eff f action
spec' = spec (\_ _ -> pure unit)

createClass :: forall props state eff f action action' state'. MonadEff (ReduxEffect eff) f => Getter' action' action -> Getter' state' props -> Spec props state eff f action' -> ReduxReactClass state' props
createClass actionLens stateLens spec_ = connect (view stateLens) reactClass
createProviderElement :: forall action props state. Store action state -> ReduxReactClass' state props -> React.ReactElement
createProviderElement store reduxClass = React.createElement providerClass { store: store } [ createElement' reduxClass [] ]

createClass :: forall eff f action props props' state state'. MonadEff (ReduxEffect eff) f => Getter' (Tuple state props) props' -> Spec props' state' eff f action -> ReduxReactClass state props props'
createClass slens spec_ = runFn3 connect_ Tuple (view slens) reactClass
where
reactClass :: React.ReactClass props
reactClass :: React.ReactClass props'
reactClass =
React.createClass { render: \this -> spec_.render (dispatch this) this
, displayName: spec_.displayName
Expand All @@ -123,20 +132,29 @@ createClass actionLens stateLens spec_ = connect (view stateLens) reactClass
, componentWillUnmount: \this -> spec_.componentWillUnmount (dispatch this) this
}
where
dispatch :: React.ReactThis props state -> f action' -> f action'
dispatch this action' = action' >>= \a -> a <$ liftEff (runFn2 dispatch_ this (view actionLens a))
dispatch :: React.ReactThis props' state' -> f action -> f action
dispatch this action = action >>= liftEff <<< runFn2 dispatch_ this

createProviderElement :: forall props action state'. Store action state' -> ReduxReactClass state' props -> React.ReactElement
createProviderElement store reduxClass = React.createElement providerClass { store: store } [ createElement reduxClass ]
createClass' :: forall eff f action props state. MonadEff (ReduxEffect eff) f => Getter' state props -> Spec props Unit eff f action -> ReduxReactClass' state props
createClass' slens spec_ = createClass slens' spec_
where
slens' :: Getter' (Tuple state Unit) props
slens' = to (view slens <<< fst)

createElement :: forall state props props'. ReduxReactClass state props props' -> props -> Array React.ReactElement -> React.ReactElement
createElement reduxClass = React.createElement reactClass
where
reactClass :: React.ReactClass props
reactClass = unsafeCoerce reduxClass

createElement :: forall props state'. ReduxReactClass state' props -> React.ReactElement
createElement reduxClass = React.createElement (unsafeCoerce reduxClass) (unsafeCoerce unit) []
createElement' :: forall state props. ReduxReactClass' state props -> Array React.ReactElement -> React.ReactElement
createElement' reduxClass = createElement reduxClass unit

createStore :: forall eff action state. Reducer action state -> state -> Eff (ReduxEffect eff) (Store action state)
createStore reducer state = createStore' reducer state id
createStore :: forall eff action state. Reducer action state -> state -> Enhancer eff action state -> Eff (ReduxEffect eff) (Store action state)
createStore = runFn3 createStore_

createStore' :: forall eff action state. Reducer action state -> state -> Enhancer eff action state -> Eff (ReduxEffect eff) (Store action state)
createStore' = runFn3 createStore_
createStore' :: forall eff action state. Reducer action state -> state -> Eff (ReduxEffect eff) (Store action state)
createStore' reducer state = createStore reducer state id

reducerOptic :: forall state state' action action'. Lens' state state' -> Prism' action action' -> Reducer action' state' -> Reducer action state
reducerOptic lens prism k action state = either (const state) (\a -> set lens (k a state') state) action'
Expand All @@ -151,13 +169,13 @@ foreign import data REDUX :: !

foreign import data Store :: * -> * -> *

foreign import data ReduxReactClass :: * -> * -> *
foreign import data ReduxReactClass :: * -> * -> * -> *

foreign import connect :: forall state' props. (state' -> props) -> React.ReactClass props -> ReduxReactClass state' props
foreign import connect_ :: forall state props props'. Fn3 (state -> props -> Tuple state props) (Tuple state props -> props') (React.ReactClass props') (ReduxReactClass state props props')

foreign import dispatch_ :: forall eff props action state. Fn2 (React.ReactThis props state) action (Eff (ReduxEffect eff) action)
foreign import dispatch_ :: forall eff action props state. Fn2 (React.ReactThis props state) action (Eff (ReduxEffect eff) action)

foreign import providerClass :: forall action state'. React.ReactClass { store :: Store action state' }
foreign import providerClass :: forall action state. React.ReactClass { store :: Store action state }

foreign import createStore_ :: forall eff action state. Fn3 (Reducer action state) state (Enhancer eff action state) (Eff (ReduxEffect eff) (Store action state))

Expand Down

0 comments on commit 9eab8c7

Please # to comment.