I have a problem similar to the one described by LutherBaker here, i.e. the load method of LoadableDetachableModel is called before the onSubmit() method of the Ajax button. Is there a model that works similar to LoadableDetachableModel but is called after the onSubmit method?
My problem: When a form is submitted, I update a record in the database. However, this update is not visible in a drop down list (that is refreshed after the onSubmit() call - it's added to target) with a LoadableDetachableModel, because this LDM is loaded before onSubmit() method of the form.
How do I make the changes done in the onSubmit() method visible in the drop down list?
Does it work with a non detachable model? Does the component have the markup id rendered out?
A LoadableDetachableModel has a cache, to avoid extra database requests and such.
If the LDM is loaded (it's load() method being called) after the onSubmit event is triggered but before you update the record, then you need to reload you LDM by detaching it, so that it will be reloaded with fresh data when the ajax target is rendered.
So try to call detach() on the model, at the end of the onSubmit() method.
If this doesn't work, then also verify that your database commands are send at the expected moment. For example, if you use some JPA flushmodes, then some database commands may be delayed to the end of the transaction, which can cause the data to be reloaded using a read query before the update query has taken place.
Related
I have a Note component where a user edits a Note. Now, in my header for the app I have a button which can be clicked and you will be sent to the base product of the company - which is an external URL.
My problem is that the componentWillUnmount function (yes, this is the "old" component based API used in this particular component) does not fire and so any unsaved changes disappear.
Ideas?
I've tried adding an event listener in componentDidMount
window.addEventListener("beforeunload", this.componentWillUnmount); but my request is still getting cancelled before the redirect. I'm using apollo with graphql endpoints btw.
I don't see how rewriting to hooks can help here.
Looks like the problem is that you, actually, do not unmount component, but leave the page. React will never know about this event.
I think, you can use another approach to keep changes: send it to the backend while user filling the note. Or, maybe, easiest way is to duplicate changes in localStorage and then recover it on new load. But it feels less secure and, of course, limited by this browser/machine
Here are a few ideas you could try:
When the user clicks the button in the header, run a function/method to save the Note before redirecting.
Add an onblur listener to Note and trigger a function/method to save when Note input field loses focus
Add an onkeypress listener and save the contents of Note with every keypress (or every x seconds to batch the updates instead).
I am currently asking myself, if useEffect is always the right way to go.
I am thinking about that, because of my current project.
Imagine you have to following:
a state query which is just a query for backend. A query also knows a sorting. This will be filled by a loadQuery method, which gets a configured query from backend. But the initial state is a empty query and NOT undefined.
a method loadData which loads data from backend of type Entity[]. This method consumes the state query. This can directly be triggered, because of the empty query which is a valid query.
a method loadView which loads views from backend of type View. A view is just a JSON, which describes the cells of a table and has a sorting property.
a useEffect, which calls loadData and loadView. As dependency, we have the query.
a useEffect function which listen on the loaded view. If the loaded view has a sorting property, then it will manipulate the query state. This will lead to trigger the useEffect again from one point above.
This is the current scenario (shortened of course).
We also have
view switch button, which changes to query again. This will lead that the useEffect for loading data will be triggered.
a infinity loading in the table (which will also call loadData with an offset),
The query can change, if the user changes the query in a FilterDialog
Sometimes the useEffect for loading data, will be triggered 3 times in a row.
Sure, a solution will be to remember a boolean if some data got already loaded or not... But since we have more then one useEffect, it is hard to do this. It is also hard to remember a boolean for this, because if a componentDidUpdate cycle occurr, then you have to reset these booleans, because everything should be executed as before. But this can lead to problems, because everything is async.
Maybe some one else had the same problem, to have too much useEffects and this leads to unnecessary things and can share his experience with me :)
In my React/ Redux app, I have a text input with its default value retrieved via Ajax call. User can edit the default value in the input, and then submit the updated value by clicking on a 'submit' link. I'm struggling with using either uncontrolled or controlled inputs for this.
Using uncontrolled input with defaultValue: the component doesn't get re-rendered when the data for default value comes back from initial Ajax call (detailed in official document here). So the input field is blank all the time.
Using controlled input with value bound to component's props: This does give the default value correctly, however since I can't change the props, the field is basically "locked". I can get around this by triggering an action to modify the global state in onChange handler and force the whole component re-rendering, but this again poses other issues. For one it seems excessive to do so in onChange, and also I don't want to commit to changing the state before user clicking the submit link.
Any suggestions what I can do here?
As the docs say, the defaultValue is only useful at the initial render. So if you want to use defaultValue you have to delay the rendering of that particular component until after the data is loaded. Consider putting a loading gif (or something similar) in place of the form for the AJAX call.
I don't think the second way - using value and updating with onChange - is as bad as you say; it's generally how I write my forms. However, the real problem here is once again the delayed loading. By the time the initial value loads in, a user may already have filled in that input, only to see it overwritten with the received AJAX value. Not fun.
The best way in my view is simply to not use AJAX. Append your initial data to the webpage itself as a global variable. This may sound like an anti-pattern but you only ever read the global once, and it saves you cost of an AJAX request. In Redux there's a convenient way of doing this which I've documented here and here.
Just to add to David's answer.
Sometimes it does make sense to make an AJAX call to load defaults.
For ex. you could be calling some service for multi language support, creating an element in the data base with default values, etc.
I would go with the value-onChange approach and prevent the user from editing the value before it is loaded:
By disabling the input until the AJAX call returns. Just bind the disabled property to some prop that shows the default was loaded.
By not rendering the input until the AJAX call returns. When you get the default value from the server you modify the state so it triggers a re render.
In my Backbone.js project I have one Model and several Views. All Views have registered callbacks for 'change:currentTextTitle' on this model:
// 'this' stands for any of the Views here
myModel.on('change:currentTextTitle', this.render, this);
Now a user performs some action, which causes the specific View to change its "current text title" field. This specific view then calls myModel.set("currentTextField", newTextValue) which in turn triggers the 'change:currentTextTitle' event calling all Views (including the one from which set() originated). Then all Views call their render callback functions.
The problem is that the render method is also called on the View from which the set()-Method was originally called, which is completely unnecessary because it is already up-to-date with currentTextTitle.
How would my Views call myModel.set() in a way that the other Views' callbacks get informed, but without triggering/calling the "source View" itself?
One workaround seems to be to pass the source view as part of the options parameter of the set() method (which gets passed along to trigger() and then along the the render() callback):
myModel.set("currentTextField", newTextValue, thisViewSetAttribute)
Then in the render callback one could check if thisViewSetAttribute != this. However, instead of implementing checks in every callback, I think it would make more sense to handle this in the Model itself by only calling the necessary callbacks and ignoring the source View from which the set() method call originated. Is this possible?
I think the 'proper' MCV solution is that your views should not know or care how the model changed, they should simply handle the change and update accordingly. If they are already current, the user shouldn't know the difference.
I definitely would not pass the source view to the model. Instead when the model changes, you could just have the view check if it is current and not re-render. But if the extra render doesn't cause any issues then just let it happen :)
In Backbone, the 'view' is both view and controller. So try to treat the change as 2 separate steps. First, convert user input into changes on the model, then as a separate step (initiated by model change event), handle that change and update the view. If each view does this, no matter how the model changes, everything will stay up-to-date.
When does the 'add' event gets fired in a collection?
I am in a notion that when i execute something like app.mycollection.create(this.newAttributes()); the add event will be fired.
Actually i do this while initializing a view
window.app.mycollection.on('add',this.render,this)
So ideally, first there should be a post request (when i do a create) and then the get request (my render function fetches rows from backend). But in network console, i get the opposite. I first see a get request and then the post request.
This made me thought that 'add' event gets fired immediately after a call to create method without waiting for creation to be complete.
This thus fetches me old data without including the data that has just been created.
Please shed some light.
You have executed render function immediately. Try update your code:
window.app.mycollection.on('add', this.render, this)
EDIT
Take a look
Creating a model will cause an immediate "add" event to be triggered
on the collection, a "request" event as the new model is sent to the
server, as well as a "sync" event, once the server has responded with
the successful creation of the model. Pass {wait: true} if you'd like
to wait for the server before adding the new model to the collection.