-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Usage discouragement in readme #26
Comments
Not complete rubbish! Actually your bug report here is more informative than either the Reason or Reductive documentation as to what the limitations of Reason's reducerComponent are, what an application which opted not to use Reductive would look like, and what some indications for the use of Reductive might be. |
Also the more I think about this the more I disagree with the conclusion that Reductive probably isn't necessary. If you just create a global compositor/store component, you are indeed stuck passing around global state as props through a chain of components that may not actually depend on them. One of two things will happen:
This wouldn't be particularly important for regular React, since most regular React components (React.Component and stateless functional components) rerender regardless of prop equality, but it is important to ReasonReact, which by design is meant not to have the fundamental problems that have forced regular React to be unable to make those optimizations. |
This is a great discussion to be had! I'm a huge fan of Redux. In my work, it has offered me these advantages: Preventing prop-passing and unnecessary coupling via propsAs mentioned, passing a bag of the entire state into a component which needs global state feels unwieldy. It tends to yield a pattern of every component in the app needing access to the global state object, which is unnecessary when we have React context. Common idioms + MiddlewareThe redux community sharing ideas, patterns and libraries involving middleware have been a huge productivity boon for me. Examples have been things like Reductive could actually enable the same type of patterns. Leveraging Reason's extensible variants and possibly GADTs, there's nothing preventing Reductive from allowing third-party actions into the system while maintaining type safety. Redux DevtoolsThis is simply something I cannot imagine living without on the frontend. It has saved me so much time, and made debugging my Redux application so much easier. Unfortunately, Reason+Bucklescript does not have the means for this yet. I've opened rescript-lang/rescript#2556, but there's so much work on Reason/Bucklescript core teams' plate as it is. Redux enables the above patterns successfully. While ReasonReact may be able to prevent unnecessary re-renders while passing down the entire state object as a prop (or even as context), the common idioms and dev-tooling built around those idioms in Redux offer significant value to me. I'm eager to improve this library, but I view it futile until there are dev-time printing mechanisms available in Bucklescript. To the point of the bold warning that "you may not need this library", I feel it's meant to bring about critical thought like this as to what is state in this language and ecosystem, and how can we leverage these tools to come up with something better. I feel Reductive could evolve to take the best of both worlds (Redux and ReasonML), but there could be something even simpler and better that we think up. |
Well, this is a good warning that should be at the top of the Redux repository as well frankly. Small apps are better off with component state and newcomers to React should definitely start there. What I get from the README, on the other hand, is "you don't need a state manager because Reason". This is confusing and frankly, just wrong. Nothing about Reason/OCaml changes any of the very excellent reasons articulated so well in this issue. In fact, it makes the project look a bit out-of-touch with the purpose of a state management framework and makes me wonder if it is implemented correctly. For example are components only re-rendered when the part of the state graph they care about is changed? Since there is mention of no higher-order connect, and no acknowledgement that such a feature is desirable, I have to assume that no, this project doesn't understand why that feature would even exist, and I move on to the next language/framework. |
Oh jeez, I made a mistake, mostly because I'm looking at Reason from the perspective of thinking about using it, rather than as an experienced user already. I thought Reason components would not adopt the React pattern of always rerendering no matter what. Unfortunately, they do. See https://github.com/reasonml/reason-react/blob/9e344b8a059ca82e88ca575bebc60abb9deac0d8/ReactMini/src/React.re#L134 and its usage just below. So not using Redux/Reductive is just a performance bomb waiting to go off, for both React and ReasonReact. I'd put the warning on React instead (this library is just a child's toy without a state management utility!). A solid piece of JS infrastructure should be simple when used the recommended way: i.e. when you follow best practices it should shield you from the nastiest complexities of lower layers and you should get a performant app which you won't need to rewrite (as in the transition from setState to Redux) when your needs become more complex. |
@conartist6 From a quick read of |
I now agree @jeremyjh. So the usage discouragement is actually a great point. There isn't any purpose or intent to what this code does. Edit: aside from allowing Reason code access to state already stored in Redux, presumably by code not written in Reason, and to enable dev tools. |
I haven't started using ReasonReact or Reductive (disclaimer), but I'm super glad I read this. But it is true, writing So that seems it a bit of an extreme conclusion there, @conartist6 ?
|
Well, you create the rather unpleasant issue that if you forget to adjust shouldComponentUpdate when you use a new piece of state data you'll get stale data. However this does make clear to me one thing that Reductive does in this situation, which is to allow a child component to receive updates on a piece of state that their parent elected not to update on. If you were just passing your global state object around through props, that would not be the case. |
That's right. |
I agree with a lot of ideas presented here. ReasonReact looks very promising to me but the main limitation that I don't see clear answers for is how to manage large apps with complex state trees. Redux provides a strong answer for this in the traditional React world, which to me is not matched by managing app state in reducer components in ReasonReact. The following are some of the main considerations I have which currently prevent me from considering ReasonReact for a complex app:
I think in the React ecosystem there was a timeline where initially React did not endorse a clear implementation for managing global state, instead suggesting a pattern (Flux). As developers saw the limitations in managing application state in React components, they innovated to create solutions and this has produced everything we have now: Redux, React-Redux, various middlewares, devtooling, and so on, which provide great choices for building a complex JavaScript application. I think ReasonReact will need a similar progression for the community to begin adopting it as the foundation for their applications. Maybe the solutions will look different, but I think the main principles of decoupling state and side effect management from the component/view layer and providing composition so the these layers are scalable, are very important. An example application where I think these considerations start to make sense are say an app with several hundred components, 50-100 Redux stores, and maybe 50-100 different HTTP methods that send and receive data from various APIs. |
This is a great discussion. I've been using React and Redux for years now, both in production and for fun. I understand why we use the flux pattern in React through Redux, but what I can't understand is why, with Reason, we don't use proven FP patterns instead of leaning on the old way of doing things... We should be using:
With the extra layer for manipulation (point 3), data fetching, randomness, ffi and the Blizz Gods only know what else; combined with real world FP ideas you can have an amazing experience and very powerful type safe framework. While still being very easy to learn. Easier than Elm, much easier than PureScript, might even be easier than normal React and Redux 'cause of that whole "don't pass lambdas as event handlers" business. That stuff is silly hard! Conclusion: I really think the community is searching for an alternative to JavaScript. I totally agree that OCaml is way better than Haskell inspired languages for beginner, intermediates and maybe even experienced programmers. Elm takes another route and just doesn't give you the power you really need to express yourself in your applications. ReasonReact should be a home run, the language is great, react is great, FP is awesome...I smell a winner! I apologise in advance for intruding on this thread. I hope I haven't offended anyone. In any case, just my two cents. (Extra cents: |
|
In case anyone is interested, I attempted to address the render issue in pull request #35. Please let me know your thoughts. |
Tried to remove the warning, but it seems it's been discussed and honed to its current state. For now, we can describe why reductive is useful, and let people decide for themselves. |
Hey folks - sorry not for weighing in on this sooner. The message is indeed put in place so that people actively think about their use case before reaching for this (or any other) state management library.
Who makes the decision about when/if this is unhealthy? I personally think that no amount of props passing is unhealthy, but you might feel differently. I do think that implying that it is unhealthy is biased toward libraries like redux unnecessarily. Someone with more context on what they are building should make those decisions.
Again I feel like this is pretty subjective. I like my components to loudly express their data dependencies and be as pure as possible. If a component is using some data (even just to pass) I want that to be visible to the people refactoring. I think the arguments for Redux that Dan makes in his blog post are pretty strong - but they are all heavily dependent on the context of the app you're building. Does your app need to persist entire state to local storage? Do you need time travel debugging? etc. We just want to make sure that this project doesn't get installed next to ReasonReact as a default without thought. I'm open to other strategies to get there, but I don't think removing this caution without doing something else is going to achieve that careful consideration we are hoping for. |
@rickyvetter These are all great considerations but none of them are unique to Reason/OCaml. The verbiage in the README is specifically telling us: You may not need global state management, because |
I don't think that is ever specifically said or implied. The caution says nothing about global state at all. The part about Reason building blocks is referring to first class algebraic data types. A lot of people are interested in Redux for the reproducible/standardized pattern of actions. This isn't necessary in JS either, but I think it's worth calling out that Reason/OCaml have this enum w/ payload first-classed into the ecosystem. I can't think of great ways to modify this, but definitely open to reviewing changes. |
Good point. I guess I never thought of using a library like |
@rickyvetter I'm confused now. If reductive is intended as a "authoritative Redux implementation", then it's (not deliberately, but in a practical sense) misleading when it doesn't support the main use-cases that Redux supports. |
@mnieber which use case(s) do you feel Reductive isn't supporting? I'm not sure I understand which cases you're thinking of. Is this in relation to a different issue? This one is specifically about the question of documentation. |
I meant the use-case where components (aka container components) connect directly to the global application state, instead of receiving this state as props from parent components. EDIT: it's a bit strange to call this a use-case, maybe "typical usage pattern" would have been a better term. By the way, there is another characteristic property of Redux which I feel is also not supported out of the box: the ability to combine reducers and still let each reducer process all actions. In Redux, this is the default behaviour. In the reductive examples, there is a dispatch mechanism that lets specific reducers respond to specific actions. |
Agreed that usage pattern is a better term for the things we are talking about. I don't think reductive stops you from connecting each of your components directly to global state without passing props or from having all of your reducers act on the same top level actions. It just doesn't show any examples like that. I don't think this is super relevant to the discouragement in readme conversation though. I think if you want to continue discussing it'd be best to open a new issue. |
@rickyvetter, I think this discussion is in the spirit of my original message and doesn't need to be moved to a new issue. The problem is not in discouragement itself. The problem is what it is implying. I read it like "You might not need this library because ReasonReact comes with reducers". But these reducers are component-level, not global, so they are not covering the main use-case as @mnieber pointed out. |
While I agree that it shouldn’t, I think the existence of reducers does cut prevent a lot of people from turning to a Redux-like library. The idea is to turn people away who are chasing fads or exciting libraries - or at least make them think! If you’ve decided that a Redux-like library is right for your app and that reducers don’t change that, then great! Also, to be honest I don’t see any of the reasons described in that article as difficult to do with hooks and some appropriate introspection into React internals. useReducer and useState allow you to do all of that cool stuff without Redux. |
The precaution is misleading. It implies that you can replace this library with ReasonReact reducers, but you can't except for some specific case. The main case because of which people turn to Redux is not supported by ReasonReact reducers. At least consider adding a bit of clarification:
I would even add a sentence or two about ease of passing props in ReasonReact as an alternative to global state. What do you think? |
I must say I'm still confused :-). It's not clear to me a) how different Reductive really is from Redux, b) how much effort is required from the user to implement typical Redux patterns with Reductive, and c) how well this would perform. However, I'm not advocating for any deep analysis of the differences (such an analysis would be nice of course but not essential). Instead, it would be great if the README could make some prediction of how much effort a typical Redux user (who is using Redux in the way that Dan Abramov teaches it to other people) would require to re-implement their (global) data store with Reductive. This would allow people who are "just" looking for the best tool to make a practical decision. |
Yeah sounds great!
I don't know if I'm capable of making such a prediction, as I am not a typical Redux user. Happy to accept PRs if someone does have/can make this estimation. a) Reductive at its core is almost identical to Redux (how different can you be in ~50 lines?) - but differs in that it doesn't encourage a lot of the same patterns. There are a couple reasons Redux might perform better:
There is one place Reductive will be faster:
|
So, it's almost 2020 and everyone's using context now. Correct me if I'm wrong but isn't the way the context behaves so that whenever anything in context changes, it will trigger a re-render in all components using the context. It feels to me, that for larger apps, it makes it pretty much unusable at least as a trivial solution. Maybe if one splits the context to multiple nested providers touching a different area/module of the app, then it might work, but if one just uses single context for the whole app state, then all changes to the global state will practically re-render the whole application every time. (Am I missing something here, honestly?). If this is the case, using reductive starts to feel pretty tempting assuming it actually solves the problem. Another thing that would be relevant to document and clarify... or at least I would find it tremendously useful... would be how to trigger side effects that are not bound to component lifecycle (i.e. useEffect). It seems to be very hard to find examples or guidelines. I see there are some ways to use thunks with reductive, but what I'm more interested in is something like redux-sagas or anything that solves the same problem. It is true that using something like these makes the code harder to follow, but it also allows ways to e.g. prevent unnecessary reloads of data independent of how components are mounted/unmounted. I guess I'm asking whether there are now any established patterns/solutions now that one year has passed since the last message in this thread. Oh... and I know there is GraphQL... but let's assume not everyone can use GraphQL :) |
@TomiS I don't have any data on this, but I expect that using context (which was always there, by the way) hasn't replaced the default patterns for connecting to the data store. Also, I think that using context should be the exception. |
@TomiS You are not missing anything, using I agree that neither I was experimenting a bit and implemented the elm architecture with Maybe it is something that might be helpful in your case? |
@MargaretKrutikova Thanks for confirming my thoughts. Also, I'm looking at the example in the repo. I'm assuming that message == action, model == state and update == reducer? Truthfully, I'm not quite understanding how the success action actually changes the data in the store though. But nice work anyway. Going to look into it in more detail later. |
Yes, those are the concepts coming from When you get back the data from the api, let update = (_, message) => {
switch (message) {
| FetchUsersSuccess(data) => ({data: Success(data)}, None)
...
};
}; |
@MargaretKrutikova Ah, you don't need the previous state at all because in this quite simple example you're replacing the whole state on each reducer call. The lack of previous state confused me. Slow brain on Sunday, it seems. :) But thanks again. |
@MargaretKrutikova |
I dont know if you follow redux toolkit maintainer Mark Erikson and his work? He recently did some work to accommodate more usecases that are currently served well by redux-saga. Please take a look at this for further reference: reduxjs/redux-toolkit#1648 Thanks, |
Hi!
Sorry if what I am about to say is complete rubbish. I am new to reason and reason-react and maybe I just didn't grasp some basic concepts yet.
In the intro you are saying:
I don't understand this point. You are talking about built-in reducers like it is an alternative to a global state. But we are mainly using redux for managing app state, not a component state. In what way built-in reducers are the replacement for redux?
If I understand Dan's point that you referenced, he is not saying that you don't need redux. He is saying that you maybe don't need redux. But there are a lot of cases when you can benefit from using it.
And then later you are saying:
We are using global state when an application starts to grow past the point when passing props down starts to be unhealthy, when you want your components to be agnostic to place and time. Imagine moving component from one part of your app to other. In redux-app you just moving it, that's it. In "classic-react-app" if you don't have single-global-compositor-component which basically have your global state, you have to tune something. Your middleman components have to know something that they don't need to know, just to pass some props down.
Am I missing something? Maybe you can elaborate a little bit more in your docs as to what real alternatives we have to reductive and why using reductive is counter-productive in reason-world?
It is kind of depressing to use a library that asks me not to use it and with bold font :)
The text was updated successfully, but these errors were encountered: