I'm looking at ReactJS as being the view in my application which has a model and a bunch of components. I can see how my model can talk to React, but how does React then talk to the rest of my application?
It's very simple to pass in data or a model into React:
var ui = ReactDOM.render(<UI model={model} />, document.getElementById('ui'));
I assumed you could set the model like so:
constructor(props) {
super(props);
this.state = {
model: this.props.model
}
}
Although state should treated as immutable according to the documents, as it's not updated instantly. So I cannot access my model through state, I have a very bad codepen example here showing how I've made it work in the wrong way. How else can I do this?
There are literally hundreds of ways to connect your React view layer with an underlying model. React itself is completely implementation agnostic when it comes to models.
However, there are some interesting approaches to this problem that have proven themselves.
One of them is Facebook's Flux pattern. It is based on the idea of data stores that are kept separately from the React components without a direct dependency on them. Instead, changes to your model data are made by dispatching actions (e.g. user logged on, filter was applied), which can be thought of like events. Those actions are then dispatched by a central dispatcher to the different stores.
Each of the stores holds a different part of the state of your application and continuously updates it according to the dispatched actions. Your React components can now pick and choose the parts of the state that they require to be rendered and subscribe to changes to this particular part of the application state.
(source: github.io)
By applying this pattern, you can keep the dependencies between the React components, which render your user interface as a function of the application's state and the stores, which represent your application's model and contain all the business logic.
This ensures both that the parts of your application stay reusable and replaceable and also that the state of your application is always consistent.
Note however that, as mentioned above, this is only one possible approach amongst many. It may be a good entry point though to understand the value behind the unidirectional dataflow and immutable data structures that make this pattern so powerful.
Related
I'm learning react and having some trouble getting my head around some code design. I have a Session component that has several Activity components in my project. In the Session component I also have a Timeline component and I need to show the total duration of all of the events. So I need to have the start time for each of the Activities in the Session component and the end time of the last. I know of "lifting state up", but I find it a little strange in my OO way of thinking from C++ to store the data in the parent and not where it "belongs". What if I later need some other data from the activity.. Then I would lift parts of the Activity data up and store some of it in the Activity.. seems quiet messy?
I thought of also having a ActivityData object and store a list of them in the Session and pass that in to each of the activity to display it there. Another way I thought about would be to have a SessionModel object and a ActivityModel object and have this seperate from the component all together and pass these models in for rendering in the component.
I am also just getting into typescript and moving my code into that. I was thinking that I could define a custom ActivityData type in the same file as the Activity component and still store the list in the Session, but then at least its more explicit that the data belongs to the activity.
What is the right (or React) way of doing this?
You are right in your thinking and yes it isn't necessarily an OO way of doing things but more of a functional programming approach
Each component can have state, and it should store that state in it's own component. If that state is then needed by another component you can pass it down as a prop. However, if it isn't a child component that needs it then like you said you should lift state up.
The next problem happens when your app starts to grow. So then you need to make some choices. You should split your components up so they don't get too big. You can have some more logical components and then have some presentational components that don't handle logic but essentially just take props and render the views from you.
However, your app is still growing so at this point you might want to invest some time in introducing a state management tool to your app. React has the context
api built into so you can use that. or you could use a library likeredux. Redux is particularly good at abstracting state to a "global" store and you each component can "connect" to the store to access the state. Really good for apps where you have lots of shared state and lots of components need to know about similar pieces of state
In terms of Typescript then it's certainly a wise idea to include that as the language is heading that way. You can keep types in the same file or keep them in the same directory but have a .types.ts file that you import into your code and declare your types/interfaces in there
I'm working on a react web app and our company bought an admin dashboard panel to save time. In this panel, they used redux to handle states and all setting parameters stored into redux ...
But really I'm confused about using Redux for each use case!
All of my requests and states are components base and are not global and those are not essential to use in other components of this app!
For example to load games list I've this code :
componentDidMount() {
this.setState({
loading: true
});
http._GET('/game/getAllGames')
.then(response => {
this.setState({
loading: false
});
this.props.dispatch(gamesListAction(response.data.result.games));
});
}
In this case, I use a response and list only on the games-list page. So I think it's not logical I store a response.data into redux and then get all data with connecting and another process...
Or another example, for inserting forms I'll never need to store forms insert or API response because it's inserted and not getting data from API!
I've read about redux but I think redux is not suitable for every app and use case and I should not store single-use responses to redux.
The reduxjs FAQ describes it the best.
In general, use Redux when you have reasonable amounts of data changing over time, you need a single source of truth, and you find that approaches like keeping everything in a top-level React component's state are no longer sufficient.
Redux is nothing but a state mangement tool and you need more compelling reasons to use it:
In your case, firstly:
React's setState will bloat the component overtime with growing requirements and it is much
harder to maintain. Separating out the state management into a reducer
refactors nicely and makes it more readable.
Caching component state, for example if you are using a shopping cart, progressive wizards. There a is lot of to and fro in user interaction which result in state changes, in such cases. Maintaining your state in redux makes a lot of sense now.
Deeply nested components passing on state and lots of props to their children quickly bloat with growing requirements. This is a classic scenario with lot of wrapper components (especially with UI details). A better way would be to refactor and directly connect low level components to redux (transform into a container. Read: presentational-and-container-components)
Application state mapping to multiple container components. For example, if you had your getAllGames loading state to be represented in some progressive-loader-component somewhere out in your app. Using redux will ease your pain sharing data between this components.
Useful when building root level components which can possibly have interactions from everywhere. Like your useraccount component, modal, alerts, loaders.
You will realize, you are writing some boilerplate code with actions and reducers. Though overtime this is much better than reacts state management which can progressively grow complex within stateful components quickly.
Yes, You are right redux is not suitable for every application, as per my knowledge redux or state management is used mainly if you want to show the user previous state instead of empty when some operation is happening
Here is a bit detailed explanation about when to use redux
https://medium.com/#fastphrase/when-to-use-redux-f0aa70b5b1e2
Happy Coding !
Before proceeding, I'd like to point out that the title of this question was rather difficult to phrase. If a more suitable title should be used, please let me know so that I may change it and make this question more useful to others.
OK, on to the problem… I am currently working on a React/Redux project. A design decision I made was to manage app state and UI almost entirely with (hierarchical) state machines, for a number of reasons (which I won't delve into).
I have taken advantage of Redux to store my state tree in a substate called store.machine. The rest of the Redux substates are then responsible for storing app 'data.' In this way I have separated out the two concerns so that they don't cross boundaries.
Extending from this, I have also separated concerns on the (React) side – using 'state components' and 'UI components.' State components deal almost entirely with the flow of state, while UI components are those components that get rendered on the screen.
I have three types of state components:
Node: This kind of state component deals with state branching. It determines which component should be rendered based on its current state (a form of delegation).
Leaf: This kind of state component exists at the leaves of the state tree. Its job is merely to render a UI component, passing along the necessary 'dispatch' callbacks responsible for updating the state tree.
Container: This kind of state component encapsulates a Node and UI component to be rendered side-by-side.
For my circumstances, we're only concerned with Node and Leaf components. The problem I'm having is that while the UI components are being rendered based on 'leaf states,' there may be scenarios where 'higher-level' states could factor into how the UI should be rendered.
Take this simplified state structure:
AppState starts off in the Home state. When the user clicks the login button, a to_login action is dispatched. The reducer whose duty is to manage AppState will receive this action and set the new current state to Login.
Likewise, after the user types their credentials and validation is done, either a success or failaction would be dispatched. Again, this gets picked up by the same reducer who then proceeds to switch to the appropriate state: User_Portal or Login_Failed.
The React component structure would look something like this:
Our top-level Node receives AppState as a prop, checks what the current state is and renders/delegates to one of the child Leaf components.
The Leaf components then render the concrete UI components passing along callbacks to allow them to dispatch the necessary actions (described above) to update state. The dotted line represents the boundary between 'state' and 'ui,' and this boundary is only crossed at Leaf components. This makes it possible to work on State and UI independently, and is therefore something I would like to maintain.
Here is where things get tricky. Imagine for the sake of argument we have a top-level state to describe the language the app is in – let's say English and French. Our updated component structure might look like this:
Now our UI components would have to render in the correct language, even though the state describing this is not a Leaf. The Leaf components that deal with the rendering of UI have no concept of parent states and therefore no concept of the language the app is in. And thus, the language state cannot be safely passed to the UI without breaking the model. Either the state/UI boundary line would have to be removed or parent state would need to be passed down to children, both of which are terrible solutions.
One solution is to 'copy' the AppState tree structure for each language, essentially creating a whole new tree structure per language… like so:
This is almost as bad a solution as the two I described above, and would need an escalating number of components to manage things.
The more appropriate solution (at least when dealing with something like languages) is to refrain from using it as a 'state' and instead keep some 'data' about it. Each component can then look to this data (either a currentLanguage value or a list of messages pre-translated in that language) in order to render things correctly.
This 'languages' problem isn't a very good example because it can be structured as 'data' rather than 'state' quite easily. But it served as a way to demonstrate my conundrum. Perhaps a better example is an exam that can be paused. Let's take a look:
Let's imagine the exam has two questions. When in a 'paused' state, the current question gets disabled (i.e. no user interaction can be done). As you can see above, we need to 'duplicate' leaves for each question under Playing and Paused so that the correct state can be passed along – something that is undesirable due to the reasons I mentioned before.
Again, we could store a boolean somewhere that describes the exam's state – something that the UI components (Q1 & Q2) can poll. But unlike the 'languages' example, this boolean is very much a “state” and not some piece of “data.” And so unlike languages, this scenario demands that this state be kept in the state tree.
It is this kind of scenario that has me stumped. What solutions or options do I have that could allow me to render my questions while utilizing information about our app's state that is not contained in a Leaf?
Edit: The above examples all use FSMs. In my application I have created some more advances state machines:
MSM (multi-state machine): Container for multiple state machines that are active simultaneously
DSM (dynamic state machine): A FSM that gets configured at runtime
DMSM (dynamic multi-state machine): An MSM that gets configured at runtime
If either of these types of state machines can help to provide a solution to my problem, please feel free to let me know.
Any help is much appreciated!
#JonasW. Here is the structure utilizing an MSM:
Such a structure still wouldn't allow me to get the 'pausable' state information over to the questions.
Let's try to propose a solution for your architectural problem. Not sure if it will be satisfactory since I am not fully confident in my understanding of your problem.
Let's take your problem from the point you start having real problems, the Exam component tree.
As you stated, the problem is that you need to replicate your leafs in each possible 'Node State'.
What if you could make some data accesible for any of the components in the tree? For my this sounds like a problem that could use the Context API that React 16+ provides.
In your case I will create a Provider that wraps my whole application / Branch of the tree that I am interested in sharing a context with:
In this way you could access your context from any of the components and it can be modified dynamically and through redux.
Then is just left for your UI Components to keep the logic to deal with the UI State provided or computed with the given context. The rest of the application can keep it structure without complicating the lower levels or duplicating nodes, you just need to add a wrapper (Provider) in order to make the Context available.
Some examples of people using this:
Material UI <- They pass the theme as a context and access it whenever and wherever (The theme can also be change dynamically). Very similar to the locale case that you showed. WithStyles is a HOC that links a component to the theme in the state. So that simplified:
ThemeProvider has theme data. Under it there can be Routes, Switch, Connected components (Very similar to your nodes if I understood right). And then you have components that used with withStyles have access to the theme data or can use the theme data to compute something and it is injected in the component as a prop.***
And just to finish I can draft kind of an implementation in few lines (I didn't try it out but it is just for explanation purposes using the Context explanation):
QuestionStateProvider
export const QuestionState = React.createContext({
status: PLAYING,
pause: () => {},
});
AppContainer
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
status : PLAYING,
};
this.pause = () => {
this.setState(state => ({
status: PAUSE,
}));
};
}
render() {
return (
<Page>
<QuestionState.Provider value={this.state}>
<Routes ... />
<MaybeALeaf />
</ThemeContext.Provider>
<Section>
<ThemedButton />
</Section>
</Page>
);
}
}
Leaf - It is just a container that gets questions from the state and render a question or more...
Q1
function Question(props) {
return (
<ThemeContext.Consumer>
{status => (
<button
{...props}
disable={status === PAUSED}
/>
)}
</ThemeContext.Consumer>
);
}
I hope I got your question right and that my words are clear enough.
Correct me if I understood you wrongly or if you want to discuss further.
*** This is a extremely vague and general explanation of how material ui theming works
I'm about to start a new React project and am trying to draw on my previous learnings to create some rules about how I structure the app.
Some things I believe to be true:
Redux holds the 'main' data for the whole application
Redux can hold UI state if it needs to be shared across the application (e.g. global modal windows that can be launched from anywhere)
Components can hold their own state with setState if that state doesn't need to be shared anywhere else in the app.
Stateless components should be used wherever possible
When I am creating a component that needs state from Redux, I will create FooContainer.js and FooComponent.js files - the Redux connect code will sit in the container.
There are large parts of the app that are UI heavy and have a lot of UI logic/state going on, but do not need any state from Redux.
I feel that with the liberal use of component-level state, I should be using more Container Components to compose smaller, stateless components. However, I see a lot of definitions of Container Components as "a HOC that connects to Redux"
Does it make sense to have a project that has many container components, where some are Redux connected and pass data from the store to their corresponding presentational component, and others that are not Redux connected but are just used to compose smaller components and manage local state?
If so, are there any recommended file structures, naming conventions etc to distinguish between the two?
A few thoughts.
First, it's important to understand that a "container component" is simply any component whose primary job is to fetch data from somewhere and pass that data to its children. This could mean making AJAX calls to retrieve data, or accessing Flux stores. That means that the wrapper components generated by React-Redux's connect function are "container components", because their only job is to extract data from the Redux store. It also means that components whose job is to manage UI state are also "container components". See Dan Abramov's original article on container and presentational components.
Second, it's entirely fun to use class components and functional components, as much or as little as you want. That's purely up to you.
Third, while you can define your "plain" components in one file, and "connect" them in another file, I personally tend to see that as unnecessary separation. Most of the time, a given React component will only be connected once, so it's perfectly reasonable to define the component and connect it in the same file.
You may want to read through some of the articles on Redux Architecture and Project Structure in my React/Redux links list for more information.
Revising your assumptions:
Redux holds the 'entire data for the whole application in a store.
Redux can hold UI state if it needs to be shared across the application in the store.
Components do not hold their own state they reference the main store (even indirectly using connect)
Stateless components should be used wherever possible. State is passed by the container.
When creating a component that needs state from Redux, I will create FooContainer.js and FooComponent.js files - the Redux connect code will sit in the container.
Answering your questions:
Does it make sense to have a project that has many container
components, where some are Redux connected and pass data from the
store to their corresponding presentational component
Yes - Dividing your app in smaller components with their own containers is good practice.
and others that are not Redux connected but are just used to compose
smaller components and manage local state?
Not really IMO - In Redux the state of your app should be kept only in a unique store. Still you can have smaller simple components in a container if related.
Having spent some time working with flux (both ‘vanilla' and with various frameworks including alt and fluxible) I am left with a question about best practice with regard to loading the initial state of components. More specifically about components directly accessing the store to do it.
The flux ‘model’ prescribes a unidirectional flow of data from Action>Dispatcher>Store>View in a loop, yet it seems this convention is eschewed when loading the initial state of components, most docs/tutorials contain examples where rather than firing an action to get the data, the component calls a function on the store directly (examples below).
It seems to me that components should have little/no information about the store, only about the actions that they can fire, so introducing this link seems both unintuitive and potentially dangerous as it may encourage future developers to jump straight to the store from the component instead of going via the dispatcher. It also runs counter to the ‘Law of Demeter’ which Flux is supposed to adhere very strongly to.
What is best practice for this? Is there a reason that this always seems to be the case? Its quite possible that I have missed out something fundamental, so please let me know if so!
Thanks.
Examples of components calling the store directly.
Flux React example from the fb flux repo example chat app (https://github.com/facebook/flux/tree/master/examples/flux-chat)
MessageSection.react.js
getInitialState: function() {
return getStateFromStores();
},
function getStateFromStores() {
return {
messages: MessageStore.getAllForCurrentThread(),
thread: ThreadStore.getCurrent()
};
}
Another example from the same repo for the TODOapp
(https://github.com/facebook/flux/tree/master/examples/flux-todomvc)
TodoApp.react.js
function getTodoState() {
return {
allTodos: TodoStore.getAll(),
areAllComplete: TodoStore.areAllComplete()
};
}
Example of the alt implementation of the above todo app: (https://github.com/goatslacker/alt/tree/master/examples/todomvc)
TodoApp.js
function getTodoState() {
return {
allTodos: TodoStore.getState().todos,
areAllComplete: TodoStore.areAllComplete()
};
}
and finally the alt specific tutorial:
(https://github.com/goatslacker/alt-tutorial/blob/master/src/components/Locations.jsx)
Locations.js
componentDidMount() {
LocationStore.fetchLocations();
},
It depends on how the structure of you app looks like. Often you want to fetch some data before showing something to the user. What I have found to be a good practice is to have a high end component which fires on mount an action which fetches any initial data from an API. This means that when this action is done fetching it calls the store which caches the data and then emits a change.
This change emit then sets in motion the re-rendering of the whole application.
This way you keep the uni-directional data flow. The whole point with Flux is letting the user extract the data flow functionality out of components to keep them more clean, discourage components to directly communicate with each other and decrease the amount of unnecessary properties which has to be passed around the application.
In the examples the initial state fetches some initial value from the store. This is a common way to fetch the initial value, but you could set it in the component as well. Both ways I would say is a good practice. This does not mean that every component is free to fetch whatever they like from the store.
Anyway, the goal would be to keep the code and data flow as intuitive as possible with Flux. All of this are reasons why there are so many implementations of Flux.