-
Notifications
You must be signed in to change notification settings - Fork 144
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
Unstated vs Unstated-Next #20
Comments
Have you ever wondered: "Why do I even have an Seriously, what purpose does an Imagine a world where your import React from "react"
import { render } from "react-dom"
import { Routes } from "./components/Routes"
render(<Routes/>, document.getElementById("root")) Nothing to setup, nothing to initialize, no global application wrapper. Then inside your import React from "react"
import { Router } from "@reach/router"
let Home = React.lazy(() => import("./Home"))
let About = React.lazy(() => import("./About"))
export function Routes() {
return (
<Router>
<Home path="/"/>
<About path="/about"/>
</Router>
)
} Still no global wrapper, still no initialized state, just a mapping of routes to components. But why would we want to do that? Well, for one there's no implicit dependencies on some global app state, or the way that the app is initialized. Without even seeing the These components create, subscribe, and persist any state that they depend on. They make sure to fetch any resources they need, they setup their entire environment themselves, they found ways of caching and persisting data that doesn't depend on the application being setup in a particular way. These components are super easy to test, all you have to do is render them with props and make assertions about them. Your unit tests are much more effective, in fact they feel like integration tests, because the unit you're working with is so self-contained. You can feel much more confident in your tests because external factors won't so easily break these components. When you work on components like this, you can think about just that component. You don't have to worry about the application that is wrapped around it. You're not climbing back up the tree worrying about other components, or reducers, or whatever. You can focus. Even further, what if every child component was also built like this? Self-contained systems all the way down. So no matter where you were in your codebase, you could think about just one component at a time. Every component in your app can be moved around to any other part of your app and you don't even have to think about it. Oh and did I mention that this makes code splitting so easy that you can do it to any component in your entire app in seconds? This is the better way to write React code. This is what scales really well. This is what is super maintainable long term. Everything that doesn't work like this should smell to you. But how do we get there? How do we make it so that components are self-contained systems? Especially when we have state that needs to be shared across those components? Well we can start by splitting up the idea of a global "store" for all of our app's state. We need to be able to grab just the state we need when we need it. So instead of having one "store" we have many "containers". We should also take this as an opportunity to consider the kinds of state that we have in our app. Which are vaguely these sorts of things:
Each of these have very different implications.
However, when you have the same kind of state, it tends to work in the same exact way as it does everywhere else. For example, once you've written one form, every other form is going to be pretty similar structure wise; Once you've subscribed to a websocket in one place, every other websocket is going to be pretty similar. This means that each of these kinds of states are great candidates for generic libraries that deal with them specifically, and have carefully designed APIs to solve the problems for that kind of state generically. For example:
Problem after problem, we can tackle our state management in pieces such that we have libraries that are carefully designed to solve the problem really well. Making full use of React along the way is part of it. Once we've solved enough of these problems in a generic way, you start noticing that there's very little state left over. Once you've got solutions for requesting data from the server, for handling your forms, components for all your UI state, etc-- you've covered almost everything. It's at that point that building UIs becomes a matter of just linking things together and moving things around. React Components, Context, and now Hooks are the three things you need to move those things around. Going back to my original example: import React from "react"
import { render } from "react-dom"
import { Router } from "@reach/router"
let Home = React.lazy(() => import("./Home"))
let About = React.lazy(() => import("./About"))
function Routes() {
return (
<Router>
<Home path="/"/>
<About path="/about"/>
</Router>
)
}
render(<Routes/>, document.getElementById("root")) Of course, sometimes there are things that need to be shared between your entire app. Sometimes you will put context providers above your function Routes() {
return (
<Intl.Provider>
<Theme.Provider>
<Router>
<Home path="/"/>
<About path="/about"/>
</Router>
</Theme.Provider>
</Intl.Provider>
)
} Even further, you should always keep these things within a component, and be careful how you do. Because you should be able to move them down the component tree later on if you decide.
I just want to close by saying that React is really great at this stuff if you actually try to leverage it. I think Redux sent the community down a path that was more comfortable for people who were coming from an MVC world and were scared of the number of responsibilities we were giving "components" in React. I think Redux stunted the community in that way. I would challenge the idea that Redux scales well, I've seen it used on codebases with millions of lines of code, and it was a regular source of confusion for developers at all levels. React on its own, used without heavy handed abstractions, scales quite beautifully, and you should try making better use of it. I'm going to close this issue, because I don't think there is a way of exposing state/logic back out of React without compromising what I've outlined above. I would encourage you not to write the library off on that basis and to explore how you can better use React. |
Thanks for the reply. I like the way how React hooks did. I know it is not the responsibility of this library, but this could be done before with unstated. I understand what you say and actually this is only true if everthing is React. With unstated or redux, we could have a store sharing between legacy jquery component and React Component. Sometimes we may still want store living outside any React component like what redux do, but I am not saying we should put all the things like that. Just want you to clarify the difference between unstated and unstated-next (especially what can/cant do before and what can/cant do now). the next.js with-unstated (from offical repo) example is still using the way that server will create a container itself and call the function container have to replace the state, thats could introduce a misconception container should be used outside React Component. However, with unstated-next, we cannot. |
Unstated-Next use React Hooks directly while Unstated create a Container class act like React Component but they are not equal. So you can call function of Container using after "new" outside React and then do the dependency injection back. But this cannot be done in Unstated-Next.
https://github.com/zeit/next.js/blob/master/examples/with-unstated/pages/_app.js
With Unstated, you can see the api could be persist (resetStore, initStore, as well as custom actions if exists) among server-side and client-side. But this also cannot be done with Unstated-Next.
If Unstated-Next aims to be a replacement of Unstated, this should be an issue.
If Unstated-Next is a state management library using React-Hook and have a rule that the state must live in React, then Unstated-Next does not aims to replace Unstated, the README should be updated to clarify the differences and this issue could be closed.
I think both libraries should be exist. Just like Context API + Hooks Combo cannot completely replace Redux.
The text was updated successfully, but these errors were encountered: