5 ms·
Looks pretty neat. My initial reactions: - I don't like the `@kea(...` because it's another bit of JS syntax I hadn't heard of before. Though I do like decorat
by kasbah 9y ago
Looks pretty neat. My initial reactions:
- I don't like the `@kea(...` because it's another bit of JS syntax I hadn't heard of before. Though I do like decorators in Python, I just feel like I have learned enough of JS syntax to be productive and they seem to keep adding unnecessary stuff.
- There is still duplication between the action names and the reducers. When using Redux I now always use these two functions:
function makeReducer(reducerObject) {
return (state, action) => {
if (Object.keys(reducerObject).includes(action.type)) {
return reducerObject[action.type](state, action.value)
}
return state
}
}
function makeActions(reducerObject) {
const actions = {}
Object.keys(reducerObject).forEach(name => {
actions[name] = value => {
return {type: name, value}
}
})
return actions
}
So I can just declare a single object of reducers and can infer the action names and creators from that e.g.
const reducerObject = {
increment(state, value) {
return state + 1
},
decrement(state, value) {
return state - 1
}
}
const reducer = makeReducer(reducerObject)
const actions = makeActions(reducerObject)
- munchor 9y ago>- I don't like the `@kea(...` because it's another bit of JS syntax I hadn't heard of before, though I do like decorators in Python. You can just not use the @ syntax for decorators and instead just do it manually like so: class Counter extends Component { ... } Counter = kea(...)(Counter); export default Counter;
- spankalee 9y agoI'm a big fan of decorators, but it seems like much of the React/Redux world is overly class-decorator happy. It's really common to define properties in a decorator which would be much easier to read, IMO, as static or instance properties. I'd rather read something more like this: @kea // kicks off the meta-programming to read the statics below export default class Counter extends Component { static actions = { increment: (amount) => ({ amount }), decrement: (amount) => ({ amount }), }; static reducers = { counter: [0, PropTypes.number, { [this.actions.increment]: (state, payload) => state + payload.amount, [this.actions.decrement]: (state, payload) => state - payload.amount }], }; static selectors = ({ selectors }) => ({ doubleCounter: [ () => [selectors.counter], (counter) => counter * 2, PropTypes.number, ], }); render () { const { counter, doubleCounter } = this.props const { increment, decrement } = this.constructor.actions return ( <div className='kea-counter'> Count: {counter}<br /> Doublecount: {doubleCounter}<br /> <button onClick={() => increment(1)}>Increment</button> <button onClick={() => decrement(1)}>Decrement</button> </div> ) } }
- acemarke 9y agoIt's worth noting that both the React and Redux teams generally discourage the use of decorators. Quoting the Create-React-App README: > Create React App doesn’t support decorator syntax at the moment because: > - It is an experimental proposal and is subject to change. > - The current specification version is not officially supported by Babel. > - If the specification changes, we won’t be able to write a codemod because we don’t use them internally at Facebook. > Create React App will add decorator support when the specification advances to a stable stage. In addition, use of the React-Redux `connect` method as a decorator can lead to unexpected behavior, such as `defaultProps` applying to the generated wrapper component instead of the underlying "plain" component. I'm not sure how the `kea` decorator handles that sort of thing.
- mariusandra 9y agoHey, thanks for the feedback! This is definitely something to think about, as nothing in the syntax of kea is set in stone. That said, the magic with having a big options hash in the `@kea({})` call itself is that then you can easily disconnect the logic from your components as your application grows. Here's more information about this: https://kea.js.org/guide/connected https://kea.js.org/guide/connected Also, I'm not sure you can do `this.actions` inside a static variable, but I understand the point that you were trying to make.
- spankalee 9y ago> That said, the magic with having a big options hash in the `@kea({})` call itself is that then you can easily disconnect the logic from your components as your application grows. Here's more information about this: https://kea.js.org/guide/connected https://kea.js.org/guide/connected Interesting. I still think these are better modeled (and read) as part of the class declaration. You can do the same thing with mixins: Transforming that example: import FeaturesLogic from '../features-logic.js'; @kea class CounterExampleScene extends FeaturesLogic(Component) { // no change here } Where FeaturesLogic is a JS class mixin: http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/ http://justinfagnani.com/2015/12/21/real-mixins-with-javascr... export const FeaturesLogic = (base) => class extends base { static actions = ...; } > Also, I'm not sure you can do `this.actions` inside a static variable, but I understand the point that you were trying to make. Not in the current Babel transform, but I thought it may be in the latest unified class fields proposal. I'm digging for it...
- mariusandra 9y agoHey, 1) As munchor pointed out, you don't need to use the decorators. See the "what is this @kea()" button in the first guide for more details: https://kea.js.org/guide/counter https://kea.js.org/guide/counter 2) Indeed there is duplication between action names and the reducers. This however makes it so that you can have many reducers listening to the same actions. E.g. setting `isLoading` to `false` when `actions.resultsFetched` occurs.
- nathancahill 9y agoThanks for sharing those functions. Definitely cuts down on a lot of the boilerplate I see with Redux.
- acemarke 9y agoAlso worth noting that the Redux docs _specifically_ have a section called "Reducing Boilerplate" [0] that shows how to write functions like this, and my Redux addons catalog [1] lists dozens of such libraries already. As I've said many times: it's entirely up to you how much abstraction you want to apply on top of Redux. [0] http://redux.js.org/docs/recipes/ReducingBoilerplate.html http://redux.js.org/docs/recipes/ReducingBoilerplate.html [1] https://github.com/markerikson/redux-ecosystem-links https://github.com/markerikson/redux-ecosystem-links
- kiliancs 9y agoIn my experience action creators and actions in the reducer don't necessarily map 1 to 1, and often don't. I think of action creators as the API to the reducer. This makes a lot more sense when using redux-thunk and similar, of course.