What is the most performant way to handle the following situation? Is there a different way to handle this (other than the options I describe) that is more efficient? Is there a 'standard' way of managing it?
Say I have the following endpoints:
/posts
/users
GET /posts:
[{
id: 1,
title: 'Some post',
userId: 1,
...other post data
},
...more posts
]
GET /users/1:
[{
id: 1,
name: 'ExampleUser',
...other user data
},
...more users
]
I want to return a list of posts which show some basic information about the user who created them. In order to do this. I will need to fetch the list of posts and then for each post fetch the user. One user may be referenced in multiple posts.
The React components are organized as (pseudocode):
<PostsList posts={posts}>
<Post post={post}>
<UserInfo user={user} />
</Post
</PostsList>
Option A: dispatch(fetchPosts()) in PostsList, then dispatch(fetchUser(post.userId) in each Post (or in each UserInfo)
Pros: Pretty easy to implement; doesn't need specialized action (ie fetchPostsWithUserInfo())
Cons: Multiple components must be connected to the store; knowing when to render a component or a loading spinner can get into complex logic; the same user can be fetched multiple times (even if fetchUser(post.userId) dispatches an action recording which users are being fetched when the action is initiated, this doesn't seem to completely prevent duplicate calls).
Option B: handle in actions
const fetchPostsWithUsers = () => dispatch => {
dispatch(fetchPostsWithUsersStart());
fetchPostsFromServer()
.then(res => {
dispatch(fetchPostsSuccess());
const users = getListOfUniqueUserIds(res.data);
users.forEach(userId => dispatch(fetchUser(userId)));
})
.catch(err => dispatch(fetchPostsError()));
}
Pros: No duplicate calls, fewer connected components
Cons: Still have issues with lots of loading checks; possibly missing information if some calls fail; tightly couples posts call to user data fetching
Option C: Like Option B, but use a Promise.all to fetch the user data and then have only one success call (dispatch(fetchPostsWithUserDataSuccess())
Pros: Fewer loading checks, all-or-nothing so no partially missing data
Cons: All-or-nothing so if once fetch fails have to re-fetch all data; slow response time because need to wait for all individual calls to complete before any data is made available
I have attempted at length to find information on this topic, but I have not been able to find any information, which surprises me because I imagine it is a common case. It might be that I am just ignorant of the correct terminology to describe the problem, in which case I would very much appreciate knowing the correct terms.
Option A is good choice, as you'll be able to reuse each container component separately. You just need to debounce the fetch calls so the same ones don't run multiple times simultaneously.
Related
tl;dr: can cypress variables be stored in some accesible place (like this or something similar) or do I have to get into an endless callback cycle if I want to access all of them for a single usage?
Long story:
the app I'm trying to cover with cypress tests runs on many different datasets so in order to prepare the test data before the test, I usually make few API calls and I'd like to work with their results:
Example:
The test should cover a "delete task" functionality. As test data, I want to create a task beforehand over our API. To do this, I need to make these calls:
Call ".../users/me" to get my userId (one of required params)
Call ".../users" to get a list of all users for particular dataset (first id is used to filter another, that is then used for assigneeId, another required param)
Call ".../tasks" with previous id's as required parameters
I currently have custom commands that handle those specific api calls while returning the result. I then call those commands and save their return as cypress variable. Should I want to do the third api call, I have to do something like this:
cy.getUserID().then((userId) => {
cy.wrap(userId).as('userId')
})
cy.getAllUsersForTenant().then((users) => {
cy.get('#userId').then((userId) => {
const result = users.find((escalationUserId) => escalationUserId !== userId)
cy.wrap(result.id).as('assigneeId')
})
})
cy.get('#assigneeId').then((assigneeId) => {
cy.get('#userId').then((userId) => {
// do the POST call with both assigneeId and userId available
})
})
Right now it's not really a big deal but I can imagine that I'l need more than 2 variables, will I have to add more nested callbacks or is there a way to store those variables at one accessible place?
I sort of figured it out by looking at other tickets - if I use function() instead of arrow functions, it is possible to share this context.
Using Apollo cache as global store - for remote and local data, is very convenient.
However, while I've never used redux, I think that the most important thing about it is implementing flux: an event driven architecture in the front-end that separate logic and ensure separation of concerns.
I don't know how to implement that with Apollo. The doc says
When mutation modifies multiple entities, or if it creates or deletes entities, the Apollo Client cache is not automatically updated to reflect the result of the mutation. To resolve this, your call to useMutation can include an update function.
Adding an update function in one part of the application that handle all cache updates; by updating queries and/or fragments for the all other parts of the application, is exactly what we want to avoid in Flux / Event driven architecture.
To illustrate this, let me give a single simple example. Here, we have (at least 3 linked components)
1. InboxCount
Component that show the number of Inbox items in SideNav
query getInboxCount {
inbox {
id
count
}
}
2. Inbox list items
Component that displays items in Inbox page
query getInbox {
inbox {
id
items {
...ItemPreview
...ItemDetail
}
}
}
Both of those components read data from those GQL queries from auto generated hooks ie. const { data, loading } = useGetInboxItemsQuery()
3. AddItem
Component that creates a new item. Because it creates a new entity I need to manually update cache. So I am forced to write
(pseudo-code)
const [addItem, { loading }] = useCreateItemMutation({
update(cache, { data }) {
const cachedData = cache.readQuery<GetInboxItemsQuery>({
query: GetInboxItemsDocument,
})
if (cachedData?.inbox) {
// 1. Update items list GetInboxItemsQuery
const newItems = cachedData.inbox.items.concat(data.items)
cache.writeQuery({
query: GetInboxItemsDocument,
data: {
inbox: {
id: 'me',
__typename: 'Inbox',
items: newItems,
},
},
})
// 2. Update another query wrapped into another reusable method, here
setInboxCount(cache, newItems.length)
}
},
})
Here, my AddItem component must be aware of my different other queries / fragments declared in my application šMoreover, as it's quite verbose, complexity is increasing very fast in update method. Especially when multiple list / queries should be updated like here
Does anyone have recommendations about implementing a more independent components? Am I wrong with how I created my queries?
The unfortunate truth about update is that it trades simplicity for performance. A truly "dumb" client would only receive data from the server and render it, never manipulating it. By instructing Apollo how to modify our cache after a mutation, we're inevitably duplicating the business logic that already exists on our server. The only way to avoid this is to either:
Have the mutation return a larger section of the graph. For example, if a user creates a post, instead of returning the created post, return the complete user object, including all of the user's posts.
Refetch the affected queries.
Of course, often neither approach is particularly desirable and we opt for injecting business logic into our client apps instead.
Separating this business logic could be as simple as keeping your update functions in a separate file and importing them as needed. This way, at least you can test the update logic separately. You may also prefer a more elegant solution like utilizing a Link. apollo-link-watched-mutation is a good example of a Link that lets you separate the update logic from your components. It also solves the issue of having to keep track of query variables in order to perform those updates.
I have a list of tasks in vuex. I have a button in a component that will add a task. I also have a backend server that will store the task in a DB.
Right now, I have it set up like this:
Button->Ajax->Backend Response->Update Vuex->Vue Updates Frontend
What I would like is:
Button->Update Vuex->Ajax/Vue each do their thing.
The issue I have is that I don't have an id for the item until the DB creates it. I could create a temporary id locally and send that to the server as a temporary id, but that seems messy. I wouldn't store it, I would just use it in vuex to update the object when it comes back from the server.
For example, let's say the database table tasks looks like this:
id | title
How would I know that the response from the server corresponds to the object I just pushed into Vuex? I can't do a find by title because it's not unique. I can't do a find by id == null because I might be creating more than one task before I get a response from the server.
I could do this in Vuex:
id | title | vuex_id
And I could use the response from the server to find the task with vuex_id that I set and update it with the id from the database.
Feels like there should be a cleaner way to make the the frontend react instantly.
Your first approach is the default (and for me the right one), go to the server and return (if it was successfully save) the id, add it to the task object and update the store.
Your second approach it's not appropriated (also for me), what if the save fails? you already have the task on the store and updated the UI, and then the task should be removed or implement some retry request method, to maintain the task in the UI. It will be harder to test and debug, and also harder to understand if you are note familiar with the code.
Button->Update Vuex->Ajax/Vue is possible, hence the reason we have Actions to perform an async operation.
That said, you can always chain actions , if the first one returns a promise say it triggers a db save, waits for it to save successfully gets back the id and then trigger a subsequent action, if it fails you may want to handle it with retries and or whatever is appropriate, something along the lines of,
actions: {
saveInDb ({ commit }) {
return new Promise((resolve, reject) => {
// set time out below is your call to save into your Db
setTimeout(() => {
// save in db
resolve(yourId)
}, 1000)
})
},
updateEverythingElse ({ dispatch, commit }) {
return dispatch('SaveInDb').then((myNewId) => {
commit('mutationToSaveId')
}).catch((e) => {})
}
}
I may not have captured everything you said completely, but just gave a brief idea how it can possibly be done from the little i have done.
Based on the edited question
This is the reason guids exist, and hence frontend/client sends in the Id, that said, looks like it might be an identity column for Id from your question, so there is then lesser options, of which you mentioned one with vuexid , the other would be combinations of the column values like a composite key could be as simple ${title}_${you_other_column}, other than this you are probably left with option 1 of your doing.
Food for thought
Along those lines, my guess is, you are doing a batch update of Array<Tasks>, if am not mistaken, so i don't see the reason for your Vuex's mutations to be per row (per task object) and that it needs to be the entire collection , you may need to rethink that portion of it as i dont know the context and the reasoning behind updating one row at a time.
I have two actions (each one does a different REST call to a service to collect some data), and I want to create a meta-Action which essentially triggers the two actions and aggregates the results.
I am just getting started with OpenWhisk, And I pretty much know how I would do this in the given language I am using to implement actions, but I am curious what the appropriate OpenWhisk way to do this might be?
If you want to aggregate the results, there is no other way currently than the one described by you:
Create a new action, fire the two actions (blocking=true) and merge the results.
The openwhisk module on npm makes that extra-simple, as you can invoke an array of actions there:
var openwhisk = require("openwhisk")
function main(params) {
var ow = openwhisk()
return ow.actions.invoke([
{name: "action1", blocking: true},
{name: "action2", blocking: true}
]).then(([result1, result2]) => { /* do something */ });
}
Invoking the actions blockingly, makes their results available in the response vs. not using blocking where you'll only get an activation id to get the results in an asynchronous fashion.
I'm converting an existing state model to Redux and it has been painless for the most part. However the one point I'm having trouble with is converting "observed" state ajax requests. Essentially, I have certain ajax requests "linked" to other pieces of state, so no matter who modifies them they'll always be issued correctly. I can get similar behavior by subscribing to the Redux store updates, but firing actions in the listener feels like a hack.
A possible solution is to move logic to the action creator via the thunk pattern. Problem is that I'd either have to duplicate fetching logic across actions (since multiple actions could modify "observed" state), or pull most reducer logic to the action creator level. The action creator also shouldn't be aware of how the reducers will respond to issued actions.
I could batch "sub-actions" so I only need to place the appropriate fetching logic in each action "block", but this seems to violate the concept of actions producing a valid state. I'd rather have this liability at the action creator level.
Are there any generally accepted rules surrounding this? This is not a simple application where ad hoc ajax requests are made as components are interacted with, most data is shared between multiple components and requests are optimized and fetched in reaction to state change.
TLDR;
I want to fire ajax requests in response to changes in state, not when a specific action happens. Is there a better, "Redux specific" way of organizing action/actionCreators to mock this behavior, other than firing these actions in a subscribe listener?
Using store.subscribe()
The easiest way is to simply use store.subscribe() method:
let prevState
store.subscribe(() => {
let state = store.getState()
if (state.something !== prevState.something) {
store.dispatch(something())
}
prevState = state
})
You can write a custom abstraction that lets you register conditions for side effects so they are expressed more declaratively.
Using Redux Loop
You might want to look at Redux Loop which let you describe effects (such as AJAX) calls together with state updates in your reducers.
This way you can āreturnā those effects in response to certain actions just like you currently return the next state:
export default function reducer(state, action) {
switch (action.type) {
case 'LOADING_START':
return loop(
{ ...state, loading: true },
Effects.promise(fetchDetails, action.payload.id)
);
case 'LOADING_SUCCESS':
return {
...state,
loading: false,
details: action.payload
};
This approach is inspired by the Elm Architecture.
Using Redux Saga
You can also use Redux Saga that lets you write long-running processes (āsagasā) that can take actions, perform some asynchronous work, and put result actions to the store. Sagas watch specific actions rather than state updates which is not what you asked for, but I figured Iād still mention them just in case. They work great for complicated async control flow and concurrency.
function* fetchUser(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED",message: e.message});
}
}
function* mySaga() {
yield* takeEvery("USER_FETCH_REQUESTED", fetchUser);
}
Ā No One True Way
All these options have different tradeoffs. Sometimes people use one or two, or even all three of them, depending on what turns out to be most convenient for testing and describing the necessary logic. I encourage you to try all three and pick what works best for your use case.
You can use a middleware to fire up your remote actions in response to the local action.
Let say I have a local action:
const updateField = (val) => {
{type: UPDATE_FIELD, val}
}
And a input field with:
<input type='text' onChange={this.props.updateField.bind(this.val)}>
So in a nutshell when you type inside of the field it fires your action that in turn changes the state via reducer. Lets just forget how this action was passed to the component or what this.val is - we just assume this has been already solved and it is working.
All is fine about this setup but it only changes your state locally. To update the server you will have to fire another action. Lets build it:
const updateFieldOnServer = (val) => {
return (dispatch) => {
MAKE_AJAX.done(
FIRE_SOME_ACTIONS_ON_SUCCESS
).failure(
FIRE_SOME_ACTIONS_ON_FAILURE
)
}
}
This is just an simple thunk async action thats somehow makes ajax request, returns promises and does something else on success or failure.
So the problem we have now is that I want both of this actions to be fired when I change the state of my input but I can't have the onChange to take two functions. So I will create a middleware named ServerUpdatesMiddleware
import _ from 'lodash'
import {
UPDATE_FIELD,
} from 'actionsPath'
export default ({ dispatch }) => next => action => {
if(_.includes([UPDATE_FIELD], action.type)){
switch(action.type){
case UPDATE_FIELD:
dispatch(updateFieldOnServer(action.val))
}
}
return next(action)
}
I can add it to my stack:
import ServerUpdatesMiddleware from 'pathToMe'
const createStoreWithMiddleware = applyMiddleware(
ServerUpdatesMiddleware,
thunkMiddleware,
logger
)(createStore);
And right now every single time when updateField action will be dispatched It will automatically dispatch updateFieldOnServer action.
This is just example I think will describe the problem easily - this problem can be fixed in many other different ways but I think it nicely fits the requirements. It is just how I do things - hope it will help you.
I am using middlewares all the time and have many of them - never had any problem with this approach and it simplifies the application logic - you only have to look in a single place to find out whats going on.
Having modules that subscribe to the state updates and the launch Ajax requests (firing actions as they go) seems fine to me, since it puts the stores/reducers firmly in charge of triggering requests. In my large app, ALL Ajax requests and other async behaviours are done this way, so all actions can be just payloads, with no concept of 'action creators'.
If possible, avoid cascading sync actions. My async handlers never fire actions synchronously, but only once the request completes.
In my view, this is a much more functional approach than async action creators, which you may or may not prefer!
componentWillReceiveProps of react life cycle is the best place to do this. componentWillReceiveProps will be passed both new and old props and inside that you can check for the change and dispatch your action which in turn will fire the ajax call.
But the catch here is state object for which you are checking needs to be added as component's props via mapStateToProps, so that it gets passed to componentWillReceiveProps. Hope it helps!