JSX is just one-to-one syntactic sugar for a createElement call (https://react.dev/reference/react/createElement). You don't even need to use it with React, any library that defines an identical function interface will do.
> one-to-one syntactic sugar for a createElement call
Used to be, but you are right that JSX is a rather thin syntax over function calls. (The modern JSX transform now uses a _jsx and _jsxs call, depending on whether you are rendering a single child or an array of children).
I did also, many coming from classic programming that hasn't touched upon functional ways of thinking usually struggle since they're prone to shortcuts whereas React really likes you to keep everything as transformations (or as some would call it functional style).
Much React work these days is confined into components, people sadly over-do it also because they perceived Redux as overkill and try to shoehorn entire applications into core React hooks (that are mostly designed for smaller components or at most views).
This is needless when Redux (with Redux-toolkit) has recipies that solves the whole-app dataflow problems well (people have issues with Redux because their components got overly complex in the past by shoehorning component issues into Redux instead and now they're complicating things in the other direction).
First rule of not messing up React/Redux: Don't mutate data, 90% of messups I see with fresh people is breaking this. Everything should become transforms! To accomplish this the spread operator (...) in JS/TS along with map/reduce/filter are your biggest friends. (You can do optimizations but 98% of the time you won't work on big enough datasets that you need to do that)
Thus when you do setX (of state hooks) you set your transformed data, same with sending messages in Redux, your Redux reducer acts upon a message to transform the internal state.
Transforming mutations into react-compatible object creation is not always trivial, particularly with twisty object graphs. I once spent a week trying to untangle a reducer monstrosity that wished it could mutate. In the end, I don't think I totally fixed it. It can be hard.
Joking aside, you're right, often it's almost easier in recursive cases to just go with flat object lists and keeping identities on objects and keep parent id's around.
In my view, React is a theoretically beautiful piece of software, and if you're planning on building a synchronized networked system keeping in line with what makes it beautiful will give you it almost for free.
For many practical cases (especially smaller SPA's/pages) however it's probably better to just go with Vue or similar and mutate away and let the system handle all update checks for you instead.
Hooks making things complicated, before that it's actually simple, let's try to talk with class-based component.
There's only two kinds of data inside a component (class-based), first is props. Props is, similar with html attribute, are given to the component by the caller / owner / parent. `<input type="password"/>` has type as the props key, and "password" as it's value.
State is data that lives inside a component and cannot in any way accessed by the caller / owner / parent. It can only be accessed by the component itself, or it's children if the component decided to pass it. In general, you'll use very few components with state in a single application unless you know what you're doing with it.
So about the data flow, generally data is handled (can be via state or state management libs, see later section) by higher ups (parent) component, and passed to it's children via props. Now the parent component, together with the the value passed, also pass function. The function can be called by the children to update the parents state.
As for data management, there's 2 way I usually handle it. First is using state management like mobx and passing it via several ways, usually using react context. State management libraries usually has section on this.
Second is to declare the data I needed on top most components (that's relevant to the data itself) via react context, and pass them either via props or make children access them via react context.
I just learned React/Nextjs for a take home coding interview assignment. I used useState a few times. Why would I need redux when there’s useState and useContext?
Other than being overly verbose, I think React is ok after building my website with Sveltekit. There just seems to be way more support for off the shelf react components and 99% of jobs ask for React.
Don't use redux alone, use redux toolkit or mobx which are more simpler and newer. Sure, you don't need them and can be replaced with useState instead.
They shine when you need / can separate the business logic with ui. Think about state management as "backend" for your frontend.
What props they have, whether a function will change which props, etc, are belonging to state management. Heck, even the state management can be done in separate project by different team.
The best thing, in a bigger org / bigger project, you can have 2 state management code. One is mock, for frontend to develop, which has no dependency with servers, and another is the real one, that really call the backend server for real data.
Data flows top down, from parents to nested children, either explicitly via props or made available as context by parent providers. If data needs to be sent upwards, the parent component must send down a callback which the nested component can call.
Hooks are frankly something that you just have to put work into learning though as they can’t be succinctly explained. It’s worth the effort though.
Where ordinary programming paradigms result in tangled and messy state, and pure functional paradigms try to pretend state doesn’t exist and stick it all in a monad, hooks provide a framework to handle state in a way that composes much like pure functions do.
React `createElement` is not the same as `document.createElement`. The exact behavior of react elements still has a lot of complexity IMO. A react element is not analogous to a DOM element. It's more like a factory for creating a DOM elements. You can put the same react element in a document twice, no problem. That doesn't work for DOM elements. That's just the tip of the iceberg.
This is because you define what you want the components result to be. But not how to do it.
The return of a component render is a description of more work to do. When that’s done is up to reacts scheduler.
Once react has rendered a tree it then commits it to the dom.
Manipulating dom elements at runtime is imperative work. Doing it server side you get the same declarative capability as react, but no runtime behaviour.
React elements not being 1:1 with the DOM is what enables us to also target native, tuis, and so on.