I'm having problems understanding what is the best way to do this using the Flux pattern. Say for example that I have a userStore and I listen to it. Once it changed, I need get the user.name and access colors[user.name] - but the colors object comes from another colorsStore store I have. Here's the essence of it:
var self = {};
userStore.addListener(function(user) {
// dependency with the colors store
var color = self.colors[user.name]
})
colorsStore.addListener(function(colors) {
self.colors = colors;
})
actions.getUser() // modifies userStore
actions.getColors() // modifies colorsStore
The problem is that the two actions are async (they get the data from an AJAX call for instance). With this in mind, the userStore might change before the self.colors variable is populated from the other store.
How is this handled using the Flux pattern? Does the Dispatcher help with this somewhat? Sorry but I'm new to the Flux pattern. Intuitively I would simply call the async actions in the appropriate order such as:
actions.getColors() // need to populate self.colors before running getUser()
.then(actions.getUser())
But was wondering if there was a more Flux-way of doing this.
Your setup is fine from flux perspective.
Your component needs to be able to handle different possible (stores) states generated by your actions, which could possibly include:
user store has old/no data, colors store already has newest data
user store has newest user data, colors store still has old data
If you want any of these states to be visible to the user in some way (eg show loading indicator, show old color/ default color while waiting for newest color), then the react-flux way is to deal with these states inside your component.
If you do not want to show anything about these states to user, you have two options:
inside your component, fire the actions.getUser() from inside the colorStore listener function (quick and dirty solution)
change the setup to prevent the unwanted store state to trigger component update
For the second solution, you could typically do:
have you component fire both actions
both listeners trigger the same function getStateFromStores()
this function fetches state from both stores, and only does component update (setState()) if user and colors match
That way, your async calls can come back in any order.
If I understand your problem correctly, you can use waitFor for this case. Also there's the discussion about "waitFor vs combining stores into one", so a combined store can solve your problem as well.
Related
We are using server-side rendering and are using preserveState for all our vuex modules to persist the state when navigating from one page to the next.
However, we have a specific store wherein we don't want to persist some properties. Is there a way to achieve this in Vuex store? E.g.
#Module({ namespaced: true, dynamic: true, store, name, preserveState: true })
class SampleModule extends VuexModule {
propertyOne: {}; // we want to persist this
propertyTwo: {}; // we want to persist this
propertyThree: {}; // we DO NOT want to persist this
propertyThree: {}; // we DO NOT want to persist this
}
The only way we can think of for now is to create a separate store for those properties we don't want to persist. But this is not ideal as these properties are quite related and that the only difference is whether or not we want to persist them.
Is there a better way?
Yes the current way it's being handle in my recent project is to create an action which takes a payload that will be use to mutate the state back to it's default value whenever I redirect to another page..usually I'll suggest you called such action inside the destroy life cycle hook of the components unloaded when you redirect to another page. Another method I know of is to use vuex subscribe method...https://vuex.vuejs.org/api/#subscribe. This may seem like an over kill but it can also work depending on your needs.
If I have some time tomorrow, I may attach some code samples for you to look at.. apologies, it's 3am and I just gotta sleep....ciao
First off some description of what I need to achieve. I show information in front-end (React) that mostly corresponds to database rows and the user can do regular CRUD operations on those objects. However, I also add some dummy rows into the JSON that I send to front-end because there are some objects that are "defaults" and should not be inserted into the database unless the user actually wants to modify them. So what I need to do is once a user wants to modify a "default" object in front-end, I first have to POST that object to a specific endpoint that copies it from constants to the database and only then follow that up with the request to modify that row.
Secondly, the architecture around this. For storing the state of the rows in front-end I'm using Redux via easy-peasy and I have a thunk for doing the first saving before modifying. Once a user wants to edit a "default" object anywhere in the UI (there are about 20 different ways of editing an object), the flow of the program looks something like this:
User edits something and presses "save"
Thunk is called in the save function and awaited
Thunk POSTs to backend to insert the object into database and return the corresponding row
Backend responds with the ID-s of the rows
Thunk calls action and updates these objects in store with correct ID-s
Thunk returns and the function pointer moves back to the modifying function
The modifying function makes another request with the correct ID-s
The modifying function updates the store with the modified values
Now, the problem I run into is from step 5 to 7, because the component looks basically like this:
const Foo = () => {
const insertToDatabaseIfNecessary = useStoreActions((actions) => actions.baz.insertIfNecessary)
const items = useStoreState((state) => state.baz.items);
const onSave = async () => {
await insertToDatabaseIfNecessary();
// do the actual modifying thing here
axios.post(...items);
}
return (
<button onClick={onSave}>Save!</button>
);
}
If you know functional components better than I do, then you know that in onSave() the insertToDatabaseIfNecessary() will update the values in Redux store, but when we get to the actual modifying and post(...items) then the values that are POSTed are not updated because they will be updated in the next time the component is called. They would be updated if this was a class-based component, but easy-peasy has no support for class-based components. I guess one way would be to use class-based components and Redux directly but I have feeling there might be a different pattern that I could use to solve my issue without resorting to class-based components.
The question: Is there a sane way of doing this with functional components?
Thunks in easy-peasy can handle asynchronous events, so you should put your axios post in there e.g.
insertToDatabaseIfNecessary : thunk(async (actions, payload) => {
// First update the data on the server
await axios.post(payload.items);
// Assuming that the post succeeds, now dispatch and action to update your store.
// (You'd want to check your post succeeded before doing this...)
actions.updateYourStoreData(payload);
})
This easy-peasy thunk will wait for the async post to finish, so you can use the action as follows in your Foo component:
insertToDatabaseIfNecessary();
You will not need to await it or use the onSave function in your Foo component.
My understanding of state variables in React is that only data that changes as a result of user interaction or asynchronous data flow should be stored in state. What if I need to request list data from several APIs and then combine these to make a new master list to serve as a constant source of truth that may need to be used by various component methods during phases of user interaction? What is the correct place to store unchanging data like this? It's tempting to put this data in state for easy access but that doesn't seem right. Thanks.
You don't need to use redux, in this case. It's OK to save your API data call to state, even though the data won't change. If you don't want to save it to state you can save it to a static or global variable.
class App extends React.Component {
API_DATA = []; // save data here
state = {
api_data: [] // it's ok too
}
componentDidMount = () => {
// call to api
// save data to API_DATA
this.API_DATA = data;
}
}
This is where something like React hooks or Redux would come in handy. If you're unfamiliar, both can be used to globally store state. Redux would be better for high frequency changes but it sounds like hooks would be best for your situation.
There are plenty of tutorials out there for this on Youtube, the React subreddit, etc.
I am new to React and I don't know what's the best way to do this.
I have a list of cars and on clicking each row it should show slide to full page details of that car.
My code structure is:
I have App which renders two components. CarList and CarDetails. Car Details is hidden initially. The reason I rendered carDetails in app is because it's a massive fix template so I would like to render this once when app is loaded and only update it's data when each row clicked.
CarList also renders CarRow component which is fine.
Now my problem is I have a getDetails function on CarRow component which is making a call to get the details based on the car id. How to get carDetails component data updated ? I used
this.setState({itemDetails:data});
but seems state of the carRow is not the same reference as state in carDetails.
Any help?
This is a fundamental issue that lots of thought and man-hours has gone into in order to try and solve. It probably can't be answered, except on a surface level, in a StackOverflow post. It's not React-centric, either. This is an issue across most applications, regardless of the framework you're using.
Since you asked in the context of React, you might consider reading into flux, which is the de-facto implementation of this one-way data-flow idea in concert with React. However, that architecture is by no means "the best". There are simply advantages and disadvantages to it like everything else.
Some people don't like the idea of the global "event bus" that flux proposes. If that's the case, you can simply implement your own intermediate data layer API that collects query callbacks and A) invokes the callbacks on any calls to save data and B) refreshes any appropriate queries to the server. For now, though, I'd stick with flux as it will give you an idea of the general principles involved in having the things that most people consider to be "good", like a single source of truth for your data, one way flow, etc.
To give a concrete example of the callback idea:
// data layer
const listeners = [];
const data = {
save: save,
query: query
};
function save(someData) {
// save data to the server, and then...
.then(data => {
listeners.forEach(listener => listener(data));
});
}
function query(params, callback) {
// query the server with the params, then
listeners.push(callback);
}
// component
componentWillMount() {
data.query(params, data => this.setState({ myData: data }));
},
save() {
// when the save operation is complete, it will "refresh" the query above
data.save(someData);
}
This is a very distilled example and doesn't address optimization, such as potential for memory leaks when moving to different views and invoking "stale" callbacks, however it should give you a general idea of another approach.
The two approaches have the same policy (a single source of truth for data and one way data flow) but different implementations (global "event bus" which necessitates keeping track of events, or the simple callback method, which can necessitate a form of memory management).
Where do sockets fit into the Flux unidirectional data flow? I have read 2 schools of thought for where remote data should enter the Flux unidirectional data flow. The way I have seen remote data for a Flux app fetched is when a server-side call is made, for example, in a promise that is then resolved or rejected. Three possible actions could fire during this process:
An initial action for optimistically updating the view (FooActions.BAR)
A success action for when an asynchronous promise is resolved (FooActions.BAR_SUCCESS)
An error action for when an asynchronous promise is rejected (FooActions.BAR_ERROR)
The stores will listen for the actions and update the necessary data. I have seen the server-side calls made from both action creators and from within the stores themselves. I use action creators for the process described above, but I'm not sure if data fetching via a web socket should be treated similarly. I was wondering where sockets fit into the diagram below.
There's really no difference in how you use Flux with WebSockets or plain old HTTP requests/polling. Your stores are responsible for emitting a change event when the application state changes, and it shouldn't be visible from the outside of the store if that change came from a UI interaction, from a WebSocket, or from making an HTTP request. That's really one of the main benefits of Flux in that no matter where the application state was changed, it goes through the same code paths.
Some Flux implementations tend to use actions/action creators for fetching data, but I don't really agree with that.
Actions are things that happen that modifies your application state. It's things like "the user changed some text and hit save" or "the user deleted an item". Think of actions like the transaction log of a database. If you lost your database, but you saved and serialized all actions that ever happened, you could just replay all those actions and end up with the same state/database that you lost.
So things like "give me item with id X" and "give me all the items" aren't actions, they're questions, questions about that application state. And in my view, it's the stores that should respond to those questions via methods that you expose on those stores.
It's tempting to use actions/action creators for fetching because fetching needs to be async. And by wrapping the async stuff in actions, your components and stores can be completely synchronous. But if you do that, you blur the definition of what an action is, and it also forces you to assume that you can fit your entire application state in memory (because you can only respond synchronously if you have the answer in memory).
So here's how I view Flux and the different concepts.
Stores
This is obviously where your application state lives. The store encapsulates and manages the state and is the only place where mutation of that state actually happens. It's also where events are emitted when that state changes.
The stores are also responsible for communicating with the backend. The store communicates with the backend when the state has changed and that needs to be synced with the server, and it also communicates with the server when it needs data that it doesn't have in memory. It has methods like get(id), search(parameters) etc. Those methods are for your questions, and they all return promises, even if the state can fit into memory. That's important because you might end up with use cases where the state no longer fits in memory, or where it's not possible to filter in memory or do advanced searching. By returning promises from your question methods, you can switch between returning from memory or asking the backend without having to change anything outside of the store.
Actions
My actions are very lightweight, and they don't know anything about persisting the mutation that they encapsulate. They simply carry the intention to mutate from the component to the store. For larger applications, they can contain some logic, but never things like server communication.
Components
These are your React components. They interact with stores by calling the question methods on the stores and rendering the return value of those methods. They also subscribe to the change event that the store exposes. I like using higher order components which are components that just wrap another component and passes props to it. An example would be:
var TodoItemsComponent = React.createClass({
getInitialState: function () {
return {
todoItems: null
}
},
componentDidMount: function () {
var self = this;
TodoStore.getAll().then(function (todoItems) {
self.setState({todoItems: todoItems});
});
TodoStore.onChange(function (todoItems) {
self.setState({todoItems: todoItems});
});
},
render: function () {
if (this.state.todoItems) {
return <TodoListComponent todoItems={this.state.todoItems} />;
} else {
return <Spinner />;
}
}
});
var TodoListComponent = React.createClass({
createNewTodo: function () {
TodoActions.createNew({
text: 'A new todo!'
});
},
render: function () {
return (
<ul>
{this.props.todoItems.map(function (todo) {
return <li>{todo.text}</li>;
})}
</ul>
<button onClick={this.createNewTodo}>Create new todo</button>
);
}
});
In this example the TodoItemsComponent is the higher order component and it wraps the nitty-gritty details of communicating with the store. It renders the TodoListComponent when it has fetched the todos, and renders a spinner before that. Since it passes the todo items as props to TodoListComponent that component only has to focus on rendering, and it will be re-rendered as soon as anything changes in the store. And the rendering component is kept completely synchronous. Another benefit is that TodoItemsComponent is only focused on fetching data and passing it on, making it very reusable for any rendering component that needs the todos.
higher order components
The term higher order components comes from the term higher order functions. Higher order functions are functions that return other functions. So a higher order component is a component that just wraps another component and returns its output.