Every new OSS repo's README should convince me to try it instead of the current standard tools, or at least compare it to them. In my case, I'd want to know what makes it better than Vuex.
Rationale, motivations, project state etc. are all pretty relevant to any README. In the case of a library that solves a problem which already has known solutions, providing evidence of why your solution is better seems like a no-brainer.
On the contrary, it's too narrow. It shouldn't be limited to open source. IMO any program that isn't a hobby/learning/research project should justify its existence before expecting people to use it.
A more charitable way I prefer to phrase it: ostensibly you invested time and effort and then shared it with the goal of other people using it, so investing further efforts into the readme helps you achieve this goal even though it might not be as fun as writing code.
It's like when someone shares their hobby programming language after investing 100s of hours of work yet didn't even invest 10 minutes writing a "quick start" heading of their readme. I would like to gently encourage them to reach for that low-hanging fruit that's so easy to overlook yet directly serves their goals.
It does have the key points listed in the readme. It probably is so obvious. Let me summarize them here.
1. Simplicity - The concepts of XSM are straightforward and easy to grasp. Whereas, the concepts of Vuex are not as easy to understand.
2. Vuex is for Vue only. On the other hand, XSM works Angular, React, and Vue. Learn once and be good with the 3 frameworks. Chance of code reuse with XSM.
I still don't quite understand the reasons for the amount of shit Redux gets these days–I find the code (especially when using Redux Starter Kit w/ Typescript) to be easy to debug, easy to read, and easy to maintain. To me, the boilerplate is evidence of a degree of abstraction that may not always be appropriate for every SPA, but in my experience, is still often a really useful and practical choice.
I feel like Redux is a foot-cannon. Too many people use it for simple apps where they take on all the pain but see none of the benefits. I always advise people to avoid it until you're sure you need it.
When you get a chance, please check out our new Redux Starter Kit package. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once:
It's also written in TypeScript itself, so it works well for TS usage. It doesn't do as much strict magical inference as something like `typesafe-actions`, but it should be very usable.
I recently wrote a small example "Github issues" app that's written in TS and uses Redux Starter Kit and our new React-Redux hooks API, if you'd like to see how the code looks:
Redux adds more complexity and is simply unnecessarily. You just don't need it. If your back-end is something like a wire protocol over ws where you've already structured everything as "actions" or "messages" it's a decent fit, but even then React hooks are a better choice (e.g. reducer hook). For everything else there's just no reason to use it. I would go so far as to say that use of redux is an anti-pattern.
Of course, if your app is already built on redux, that doesn't mean refactoring away from it should be a business priority, but new projects should avoid it.
I have a lots data that needs to be globally available between multiple pages and is largely interdependent (i.e. one update might affect multiple slices of the data). The data needs to be updated optimistically (i.e. we show updates to the user before the fetch succeeds and unwind if it fails) and the updates are triggered through a mix of web socket polling and user actions.
You're right that I don't need Redux for this project, but oh boy I really like using it.
> lots data that needs to be globally available between multiple pages and is largely interdependent
This is the ideal use case for react context. It's not as if redux is doing something magical to achieve that result, it uses context under the hood anyway, but it adds on the complex and usually unnecessary action/reducer abstractions. I have mentored about two dozen developers on react and redux projects over the past three or so years, I've learned this lesson the hard way.
> the data needs to be updated optimistically (i.e. we show updates to the user before the fetch succeeds and unwind if it fails) and the updates are triggered through a mix of web socket polling and user actions.
Redux doesn't address these problems specifically, but I'd be curious to understand why you think redux is of particular value for them.
> You're right that I don't need Redux for this project, but oh boy I really like using it.
Well that's fine, I'm not arguing that you shouldn't use what you like.
Not really. React.Context solves the global availability bit but it does not scale with the size and complexity of your data. You'll end up building a custom Redux store inside your Context anyways, except now with an uglier API and no devtools.
Alternatively, you could just use a bunch of independent React Contexts, but that becomes unmanageable quickly especially if the Contexts are interdependent.
> Redux doesn't address these problems specifically, but I'd be curious to understand why you think redux is of particular value for them.
The action/reducer model is a very natural way of modeling it. The business logic and data fetching layer is completely disjoint from the actual data store. We can simply describe what happened -- hey, we tried to post this data, this request succeeded, but this one failed and here's the error message -- and then let the reducers decide how that updates their state, if at all. It makes it much easier to refactor code and add new features, especially when they can just hook into existing actions.
Even if I didn't use Redux -- and to be honest I don't love the API sometimes -- I would follow the Redux patterns. When done well it's a really seamless way of managing data that would otherwise be a lot of ugly business logic spaghetti.
> have mentored about two dozen developers on react and redux projects over the past three or so years, I've learned this lesson the hard way.
If you're mentoring novice developers I don't doubt the apps they were working on didn't necessitate Redux. It's a powerful tool but it comes as a price, which is why as much as I like it I avoid recommending it to people (especially junior developers) unless they have a really good case for it.
React hooks do not solve the same problem as Redux. Hooks solve the problem: how do I share functionality (logic) between stateless, functional components. Redux is for sharing data. Besides sharing data, (React-)Redux is (or at least was) way more efficient because it can preemptively avoid rendering altogether.
> If your back-end is something like a wire protocol over ws where you've already structured everything as "actions" or "messages" it's a decent fit
Redux has been fine for every API I've every used. Maybe you've only seen crazy codebases where people write a unique action generator/action_type/reducer for every single endpoint, but that isn't exactly a problem with Redux.
I think a good bit of is it the fact that the example code on the redux website is _extremely_ verbose, and that most people who stick with redux for the long haul end up using a variety of shortcuts that make their code significantly terser yet still readable. I'm in the middle of writing a short presentation for my local jsx meetup on a set of simple rules for writing good redux code. As part of the presentation, I'm providing two repos for a webapp, one with the store written using the exact code style used by the redux website, and a second one using my suggested rules and IMO the result is pretty dramatic. Until I got to writing the 'official' style version of the app, I had forgotten just how verbose redux can be. I would NOT want to use redux if I was sticking to the 'official' example style.
I'd like to see that presentation. Got a link to the slides?
And yes, as part of our planned docs revamp, we're going to write example code that is much shorter, including use of Redux Starter Kit by default and use of the "ducks" pattern and/or "feature folders" by default.
4 lines instead of 6, just as readable to me. And frankly, I'd consider doing that as a 1-liner since there's only two attributes in the action anyway. Where I work, we have a semi-official rule of 'newlines for attributes if there are more than 3, or if the line length > 80', so our policy would write this action creator as a single line.
It spreads the responsibility for some change in state across a number of files, and generally feels really incoherent. That's what I don't like about it anyway.
My guess is that tutorials and whatnot don't include, upfront or at all, a section on when to actually use redux.
It's really easy to get the feeling EVERYTHING needs to go into global state management, from variables used once in one component on one page, down to loop counters.
The simplistic contrived examples exacerbate this. A list of todos doesn't really need to be in global state. An auth system would though. However, all the examples only show things like list views or a detail view, that don't need to be globally available.
After an horrific redux experience in a large app, I recently had to set it up on a new project, and was pleasantly surprised. Granted, everything so far has been synchronous, but hooks makes for a lot less boilerplate. Haven't even bothered with action creators, just dispatch stuff.
A unique action generator, action type, and reducer for every endpoint is the common failure mode (probably because thats what the docs do). Also, the react-redux bindings are a truly horrific API - only call connect where you absolutely must (you can can actually call dispatch on the store if you have a reference).
Why do you feel that React-Redux has a "horrific API"?
You might want to read my "History and Implementation of React-Redux" post [0] to understand why `connect` is written the way it is, as well as watch my recent talk "A Deep Dive into React-Redux" [1].
FWIW, we do now have a hooks-based API as well [2]. Do you feel that looks any better?
In general, we recommending connecting _more_ components for better performance [3], and recommend _against_ importing the store directly [4].
I find connect quite verbose for a simple 'I want this component to be able to access the userName field' use case.
However the hooks api is excellent for this.
You can also write custom hooks that pull data from redux and other sources (in my case, root level props passed from another(angular) app, that I made available using Context.
> Why do you feel that React-Redux has a "horrific API"?
`connect()` is a function that takes 4 optional arguments. The first one is a function, the second is an obj or a function that returns an object, the third is a function that takes 3 arguments that returns an object, the last is an options object that takes up to 7 params (four of which are functions)! Unfortunately, you aren't done yet, because connect returns a function! You pass your new function a component and it returns a different one. Now, you have something you can use! That is the most complicated interface I've ever seen in any library.
`connect` makes code much harder to understand for what seems to be a simple task - wire some data into a component when the data changes.
Well, bear in mind that partly, it was horrific because I was new to it, and it was very much in at the deep end.
* inconsistently named action creators defined in the same file(sometimes) as the actions
* confusing tests that I still don't understand
* the main focus being on axios requests handled in reducers rather than reducers themselves
* the two main reducers being updateData (which merged in object into the store and saveData which fired off the axios request, and every component had to implement custom logic around using these two. Connects everywhere (which a child comment warns against!)
Don't think anyone working on it had made any one decision badly - just a combination of using a new tech in a slightly non standard setting, deadlines, and a strange backend integration.
Also, I don't think any of it was Redux's fault. That link looks good, thank you, will try it out and see if it is similar to what I came up with on my own for this new project.
Not affiliated, I just want to throw in Akita https://github.com/datorama/akita from datorama, in the conversation, I have successfully used it in an Angular project, but it's framework agnostic, built on top of rxjs.
I immediately think to myself that the designer of the API either (a) values tiny function names over understandable ones, or (b) hasn't given naming enough thought to be consistent. Either way, it's not a good sign and turns me off from investigating further.
The package only exposes 9 functions, and the naming is mostly consistent. There's no benefit to publicly nitpicking function names in something with such a small API.
Honestly, no need to put much thought in gp's comment. He just wants to show off that he is an experienced engineer that values good code and readability.
Edit: for those who disagree and downvote. Not only gp's comment doesn't adress the core issue and is a direct attack to the developer, but it cannot even be accepted as valid and helpful criticism. His point b says that it is possible that the developer doesn't value consistency, while his point a says he might be consistent with short naming. These remarks make it look more like random possible remark than ones that are really specific about this project.
Whilst I'm not a fan of the name, I'm more discouraged that it needs to specify a fx at all which suggests that the library hasn't come up with a generic solution for all fx's but rather a library of quirks to abstract over a limited set of fx's.
Since it only has hard-coded support for 3 frameworks, there's no benefit to using magic strings, it would be preferable to use a typed API like:
XSM.useReact({ ... })
Where you can provide fx-specific options for each fx it supports. It also hides all the config options available behind a generic API which you have to dive into the impl [1] to see what options are available and what args each config option supports. Everyone would be far better off bringing all config options to the API Surface area, you could easily type-check all options in TypeScript which ensures correct usage of the API and let you know of any additions/removal or breaking changes.
I have changed setcfg to setup and make setcfg an alias of setup. Not that I agree with you, but I do want people like you take a closer look.
We must not judge something by its name or look, rather must judge on its merits.
Is there an actual scenario for this? People either picks Angular/React/Vue and live within their respective ecosystem, why would they accommodate themselves with a solution that compatible to all of those but not as good as other state management store that tailored for one of them?
How about if you want to maintain your website in angular, but want to reuse your state management code with a mobile app written in react-native/weex?
You'd honestly be better off rewriting it. And if that's a huge ask, you need to seriously reconsider what is 'global state' in your app.
You state management shouldn't really have all your api calls in it. Especially if you're worried about cross platform, put your api interface code into a library, call that from components or your state management. If you find your state management code seems to exist only to call out to your api, it's probably not global state in the first place.
Approximately 90% of the time, when people are posting questions about VueX, my first thought, is 'that really doesn't seem like global state'
I don't think I get it, what do you use global state for? And what do you use to query your API and store responses then?
Most of the time yeah I do use Redux (sagas) to query my API and store the result. Many of my components need access to the same data (who's the current user, what's their list of items, what's the current item being looked at...), so it's at least shared state if not global
It's super dependant on the app. however, abstracting your api calls to a single library is helpful for any app beyond the most simple todo app.
Things like the current user are 100% global state.
A list of user settings, or valid values for a dropdown that doesn't change often (like a list of timezones, or countries), totally global state.
A list of objects only shown on one page or in one component? That component can call an api library for the data itself.
State of a component? Definitely not global scope. Specific example; virtual scroll for a table. storing the offset as global scope. As soon as a second instance of the component was loaded on the same page there would be major issues.
Data that gets paginated should probably not be in global state, or cached heavily, depending on the app. A couple dozen entries is fine, but the idea of pulling every record in a list from say, an erp app, and then paginating in memory, would wreak havoc on a user's browser. And you're keeping that in the browsers session memory. Not to mention ramifications on the backend, even if it is cached. and then you need to deal with cache invalidation, and then...
It doesn't let you write generic components that you can use across all fx's, it just lets you use the same limited state API to set state in different fx's. Most of the effort with porting code amongst different fx's is refitting your logic into their different component models. So instead of using the well-tested, documented, native reactive model in each FX, yo'd be limiting yourself into using a limited, non-standard state wrapper API. I don't see any value in this abstraction myself. Forcing myself to use it would generate more abstraction/boilerplate in refactoring out custom logic so it's not tied to any FX.
If you want to write code that targets multiple JS fx's you're better off with a well thought out proper solution that lets you develop components that you can use in all major components like https://stenciljs.com or alt solution that lets you target Web Components.
This is a weird choice of technical stacks, TBH. You never-the-less need to maintain two different frameworks, the UI component will be hard to look consistent, and build/testing flows are probably different, and I have my reservation how much code can be actually shared, and to share that code what the cost you are paying.
The fact that it only works with Angular, React, or Vue is a little discouraging (or at least, that's the impression I get by the `frameworkValue` config). Was initially excited to check out a new general-purpose state management solution.
From React developer perspective this looks ugly (at least for now). It is dependant on class-based components, it will do nasty modifications to your class instances.
It implements the Realworld Example App specs. The state and API logic are factored out into an NPM package(rw-xsm-handlers). The intention is to use the same code(state management and API) for all the XSM supported frameworks to implement the Realworld Example App specs.
The React example is finished. Angular and Vue will be added later.
61 comments
[ 3.7 ms ] story [ 115 ms ] threadIt's like when someone shares their hobby programming language after investing 100s of hours of work yet didn't even invest 10 minutes writing a "quick start" heading of their readme. I would like to gently encourage them to reach for that low-hanging fruit that's so easy to overlook yet directly serves their goals.
-- https://www.dictionary.com/browse/solipsistic
... in case anyone else had to Google that
https://redux-starter-kit.js.org
It's also written in TypeScript itself, so it works well for TS usage. It doesn't do as much strict magical inference as something like `typesafe-actions`, but it should be very usable.
I recently wrote a small example "Github issues" app that's written in TS and uses Redux Starter Kit and our new React-Redux hooks API, if you'd like to see how the code looks:
https://github.com/markerikson/rsk-github-issues-experiment
In particular, here's an example of RSK's `createSlice()` API in action, with TS and some thunks:
https://github.com/markerikson/rsk-github-issues-experiment/...
This looks very nice though, avoids almost all the repetitions you usually have to write when setting up reducers and actions!
Of course, if your app is already built on redux, that doesn't mean refactoring away from it should be a business priority, but new projects should avoid it.
I have a lots data that needs to be globally available between multiple pages and is largely interdependent (i.e. one update might affect multiple slices of the data). The data needs to be updated optimistically (i.e. we show updates to the user before the fetch succeeds and unwind if it fails) and the updates are triggered through a mix of web socket polling and user actions.
You're right that I don't need Redux for this project, but oh boy I really like using it.
This is the ideal use case for react context. It's not as if redux is doing something magical to achieve that result, it uses context under the hood anyway, but it adds on the complex and usually unnecessary action/reducer abstractions. I have mentored about two dozen developers on react and redux projects over the past three or so years, I've learned this lesson the hard way.
> the data needs to be updated optimistically (i.e. we show updates to the user before the fetch succeeds and unwind if it fails) and the updates are triggered through a mix of web socket polling and user actions.
Redux doesn't address these problems specifically, but I'd be curious to understand why you think redux is of particular value for them.
> You're right that I don't need Redux for this project, but oh boy I really like using it.
Well that's fine, I'm not arguing that you shouldn't use what you like.
Not really. React.Context solves the global availability bit but it does not scale with the size and complexity of your data. You'll end up building a custom Redux store inside your Context anyways, except now with an uglier API and no devtools.
Alternatively, you could just use a bunch of independent React Contexts, but that becomes unmanageable quickly especially if the Contexts are interdependent.
> Redux doesn't address these problems specifically, but I'd be curious to understand why you think redux is of particular value for them.
The action/reducer model is a very natural way of modeling it. The business logic and data fetching layer is completely disjoint from the actual data store. We can simply describe what happened -- hey, we tried to post this data, this request succeeded, but this one failed and here's the error message -- and then let the reducers decide how that updates their state, if at all. It makes it much easier to refactor code and add new features, especially when they can just hook into existing actions.
Even if I didn't use Redux -- and to be honest I don't love the API sometimes -- I would follow the Redux patterns. When done well it's a really seamless way of managing data that would otherwise be a lot of ugly business logic spaghetti.
> have mentored about two dozen developers on react and redux projects over the past three or so years, I've learned this lesson the hard way.
If you're mentoring novice developers I don't doubt the apps they were working on didn't necessitate Redux. It's a powerful tool but it comes as a price, which is why as much as I like it I avoid recommending it to people (especially junior developers) unless they have a really good case for it.
> If your back-end is something like a wire protocol over ws where you've already structured everything as "actions" or "messages" it's a decent fit
Redux has been fine for every API I've every used. Maybe you've only seen crazy codebases where people write a unique action generator/action_type/reducer for every single endpoint, but that isn't exactly a problem with Redux.
And yes, as part of our planned docs revamp, we're going to write example code that is much shorter, including use of Redux Starter Kit by default and use of the "ducks" pattern and/or "feature folders" by default.
Could you leave a comment at https://github.com/reduxjs/redux/issues/3313 with some notes on how the current docs examples are too verbose?
If you're willing to wait a week, I could share. I'm really not ready to share it as is (still working on the code).
> Could you leave a comment at https://github.com/reduxjs/redux/issues/3313 with some notes on how the current docs examples are too verbose?
Yes, I could do that, but again I'll need at least a couple of days. It's a crazy week for me.
If you want a _quick_ example, here's one from the 'actions' section of the basic tutorial:
------
------I would advocate writing this as:
-------4 lines instead of 6, just as readable to me. And frankly, I'd consider doing that as a 1-liner since there's only two attributes in the action anyway. Where I work, we have a semi-official rule of 'newlines for attributes if there are more than 3, or if the line length > 80', so our policy would write this action creator as a single line.
export const addTodo = payload => ({ type: ADD_TODO, payload });
For that example, it's a combination of:
- ES6 was barely finalized when the docs first came out
- Questions of clarity and familiarity with arrow functions.
Fortunately, with Redux Starter Kit, you don't even have to write action creators or action types by hand any more :)
It's really easy to get the feeling EVERYTHING needs to go into global state management, from variables used once in one component on one page, down to loop counters.
The simplistic contrived examples exacerbate this. A list of todos doesn't really need to be in global state. An auth system would though. However, all the examples only show things like list views or a detail view, that don't need to be globally available.
....eventually.
Also, see the other comment I left for discussion on using Redux Starter Kit to simplify that code ( https://news.ycombinator.com/item?id=20501773 ).
A unique action generator, action type, and reducer for every endpoint is the common failure mode (probably because thats what the docs do). Also, the react-redux bindings are a truly horrific API - only call connect where you absolutely must (you can can actually call dispatch on the store if you have a reference).
You might want to read my "History and Implementation of React-Redux" post [0] to understand why `connect` is written the way it is, as well as watch my recent talk "A Deep Dive into React-Redux" [1].
FWIW, we do now have a hooks-based API as well [2]. Do you feel that looks any better?
In general, we recommending connecting _more_ components for better performance [3], and recommend _against_ importing the store directly [4].
[0] https://blog.isquaredsoftware.com/2018/11/react-redux-histor...
[1] https://blog.isquaredsoftware.com/2019/07/blogged-answers-th...
[2] https://react-redux.js.org/api/hooks
[3] https://redux.js.org/faq/react-redux#should-i-only-connect-m...
[4] https://redux.js.org/faq/store-setup#can-or-should-i-create-...
However the hooks api is excellent for this.
You can also write custom hooks that pull data from redux and other sources (in my case, root level props passed from another(angular) app, that I made available using Context.
`connect()` is a function that takes 4 optional arguments. The first one is a function, the second is an obj or a function that returns an object, the third is a function that takes 3 arguments that returns an object, the last is an options object that takes up to 7 params (four of which are functions)! Unfortunately, you aren't done yet, because connect returns a function! You pass your new function a component and it returns a different one. Now, you have something you can use! That is the most complicated interface I've ever seen in any library.
`connect` makes code much harder to understand for what seems to be a simple task - wire some data into a component when the data changes.
> Do you feel that looks any better?
Yeah!
* inconsistently named action creators defined in the same file(sometimes) as the actions
* confusing tests that I still don't understand
* the main focus being on axios requests handled in reducers rather than reducers themselves
* the two main reducers being updateData (which merged in object into the store and saveData which fired off the axios request, and every component had to implement custom logic around using these two. Connects everywhere (which a child comment warns against!)
Don't think anyone working on it had made any one decision badly - just a combination of using a new tech in a slightly non standard setting, deadlines, and a strange backend integration.
Also, I don't think any of it was Redux's fault. That link looks good, thank you, will try it out and see if it is similar to what I came up with on my own for this new project.
Edited for spacing
I do strongly disagree about the "`connect`s everywhere" thing, though. We generally recommend connecting more components, not just a few at the top.
I think that depends on how you're scoping 'redux'. There's certainly a world of difference between saga, thunk, and observable.
The package only exposes 9 functions, and the naming is mostly consistent. There's no benefit to publicly nitpicking function names in something with such a small API.
Edit: for those who disagree and downvote. Not only gp's comment doesn't adress the core issue and is a direct attack to the developer, but it cannot even be accepted as valid and helpful criticism. His point b says that it is possible that the developer doesn't value consistency, while his point a says he might be consistent with short naming. These remarks make it look more like random possible remark than ones that are really specific about this project.
Since it only has hard-coded support for 3 frameworks, there's no benefit to using magic strings, it would be preferable to use a typed API like:
Where you can provide fx-specific options for each fx it supports. It also hides all the config options available behind a generic API which you have to dive into the impl [1] to see what options are available and what args each config option supports. Everyone would be far better off bringing all config options to the API Surface area, you could easily type-check all options in TypeScript which ensures correct usage of the API and let you know of any additions/removal or breaking changes.[1] https://github.com/peterluhub/xsm/blob/master/xsm.js
Maybe that just escaped you from some reason, but I would think it's perfectly understandable for just about anyone.
Curious if anyone else here had a hard time parsing it...
I kinda missed the big picture here.
You state management shouldn't really have all your api calls in it. Especially if you're worried about cross platform, put your api interface code into a library, call that from components or your state management. If you find your state management code seems to exist only to call out to your api, it's probably not global state in the first place.
Approximately 90% of the time, when people are posting questions about VueX, my first thought, is 'that really doesn't seem like global state'
Most of the time yeah I do use Redux (sagas) to query my API and store the result. Many of my components need access to the same data (who's the current user, what's their list of items, what's the current item being looked at...), so it's at least shared state if not global
Things like the current user are 100% global state.
A list of user settings, or valid values for a dropdown that doesn't change often (like a list of timezones, or countries), totally global state.
A list of objects only shown on one page or in one component? That component can call an api library for the data itself.
State of a component? Definitely not global scope. Specific example; virtual scroll for a table. storing the offset as global scope. As soon as a second instance of the component was loaded on the same page there would be major issues.
Data that gets paginated should probably not be in global state, or cached heavily, depending on the app. A couple dozen entries is fine, but the idea of pulling every record in a list from say, an erp app, and then paginating in memory, would wreak havoc on a user's browser. And you're keeping that in the browsers session memory. Not to mention ramifications on the backend, even if it is cached. and then you need to deal with cache invalidation, and then...
If you want to write code that targets multiple JS fx's you're better off with a well thought out proper solution that lets you develop components that you can use in all major components like https://stenciljs.com or alt solution that lets you target Web Components.
Better just use React all the way.
You can do much cleaner job using React hooks.
https://github.com/peterluhub/realworld-example
It implements the Realworld Example App specs. The state and API logic are factored out into an NPM package(rw-xsm-handlers). The intention is to use the same code(state management and API) for all the XSM supported frameworks to implement the Realworld Example App specs.
The React example is finished. Angular and Vue will be added later.