I'm working in single page web application. In page i have a dropdown so based on dropdown value i will load the data in grid. The grid data is come from jqgrid component so sometime grid data is still loading and user change the dropdown so previous server request is not complete and user hit a new request to server so i want a solution for this problem. so i have decided to disable the dropdown until the data is not fully loaded so i make a variable in state with value false (first time dropdown is disabled) and when the data is loaded make it true. but problem is that i'm unable to update the state of parent from child.
i have create a function called
handleChange(e)
{
this.setState({ isDisabled: false });
}
and in my state i have set isDisabled value true. i.e means by default it is disable. In child component jqwidget data adapter in callback i'm calling the parent function.
let dataAdapter = new jqx.dataAdapter(currentComponent.state.source,{ formatData: function (data) { },
loadComplete: function (data)
{
this.props.handler(false)
//console.log(data);
}.bind(this)
i'm not putting my complete code you can check the snippets here.so how to update the parent state from child.
Related
I have a "slow" loading issue in one of my pages.
That page contains a "root" component - let's call it Container.
My Container makes 2 fetch calls upon a value selection from a dropdown list.
The fetched data (A and B) are passed to a child component (Board).
Board is built from 2 unrelated components - Player and Item and they use A and B respectively as initial data.
It so happens that the Players render process is much more heavy than Item's.
The result is that Item is rendered on the page and after some few seconds Player is rendered also.
My wish is to see both rendering at the same time - how can I achieve that?
I guess it is something should be done by Board but how?
It is also important that this "waiting" will take place every time the value from the droplist is changed (it always there for the users to choose).
Thanks!
There are a couple of ways to do this.
1) Use graphql to orchestrate the loading of data, so that the page renders when all of the data is available
2) Set state values for playerDataReady and itemDataReady. If either of these is false, display a loading spinner. When they are both true (as a result of ajax calls being resolved), then the render will do both at the same time
In the constructor...
state = {
playerDataReady: false,
itemDataReady: false,
}
:
:
componentDidMount() {
// Request player data, using axios or fetch
axios.get(...)
.then(data) {
this.data = data
this.setState({playerDataReady:true})
}
// Do the same for your item data (and update itemDataReady)
}
In your render function:
render() {
if (!playerDataReady || !itemDataReady) {
return <Loader/>
// The data has arrived, so render it as normal...
return ....
You can fill in the details
I have two components, where a user can attach an image or gif to a post. Whenever the image is clicked, a modal pop up, and I want to pass the data, back to modal, so the clicked image shows.
I have made a lifecycle hook so that I can check if props are being sent, and then set the state in my child component like such:
componentWillReceiveProps(){
if(this.props.modalImageData){
this.setState({imageData: this.props.modalImageData})
}
}
this works fine, but I'm having issues passing the state correctly.
I made a handler, where I'm passing the image data, of the clicked image into a handler, that is setting the state, for a property called imageModalData, which I'm passing to the CreateGif component:
showModalSpeceficHandler = (event, image) =>{
//console.log(event.target.value);
console.log('image data is ' + image)
this.setState({imageModalData: image})
console.log('modal image data is ' +
this.state.imageModalData)
this.showModalHandler()
}
Here I'm experiencing issues when I first console log the image, that is being passed into the function, it is correct, but afterward, when I console log the state, of the imageModalData It's displaying a random picture.
afterward, the random picture is passed back down to the child component, which is not desired, what am I doing wrong since the state is not sat correctly
Modal show={this.state.showModal} modalClosed={this.showModalHandler}>
<CreateGif onSelectUrl={this.addedImageHandler} onSelectFile={this.addFileDataHandler} modalImageData={this.state.imageModalData}/* this is where the data is passed back down */ />
Take a look at the docs for setState:
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
If you need to ensure that the update has been applied before some action is fired (like rendering your modal), you can use the setState(updater, callback) form:
this.setState(
// updater function, returning a state update
state => ({imageModalData: image}),
// callback function, executed after update is applied
() => this.showModalHandler()
);
so i have my two different views one with search on it and then after you select the data row you want it changes to the view base which shows you all the data in full. only thing is you have to be logged in to view it but when you change the url it doesnt carry over the logged in value to make it true so it logs you out and your still able to view the data how would i make it carry the data over from the first page to the second one.
doSomething(value) {
this.setState({
isLoggedIn: value
}, function () {
this.state.isLoggedIn
})
if (this.state.isLoggedIn === false) {
this.setState({
searchResults: []
}, function () {
this.state.searchResults
})
}
}
that is what sets the state of isLoggedIn on the first page how would i pass the variable through to the second page as its not a child or any relation to this component.
use redux and create a global store to manage state
I have a component that on a swipe will send an action upward to the parent route/controller to handle some ajax functionality. This component has some UI that gets set to a loading state to show the user things are happening. When working gets set back to false the loading animation in UI stops and user interaction can happen again.
callAjaxAction() {
this.setProperties({working:true});
Ember.RSVP.cast(this.attrs.action()).finally(() => {
this.$('.draggable').animate({
left: 0
});
this.setProperties({working:false});
});
}
In this case the controller catches the action specified on the component definition and calls an ajax function to get some data to display in the page
// in the controller action
return Ember.RSVP.Promise((resolve,reject) => {
Ember.$.ajax({
type: 'get',
dataType: 'json',
url: `http://***/api/paysources/user/697?key=${ENV.APP.api_key}`
}).then((response)=>{
this.setProperties({
'cards':response.user_paysources,
'showCards': true
});
},(reason)=>{
reject();
this.get('devlog').whisper(reason);
})
})
This will show a new popup type of component that will allow the user to pick a card to pay with. If the user clicks away, or if they click a card and the ajax action completes I need to reset the UI not only on this page (change it so it says the cart has been paid for) but also send the swipe component (the one that now has a loading animation) something that tells it it's done loading.
Basically, the way I see it in my head, is there a way to fire an action on a component from the parent controller/route?
To answer your question "is there a way to fire an action on a component from the parent controller/route?": No there is not. However there are two idiomatic ways I can think of for doing this in Ember:
You could potentially get around it by moving the Ajax request into the component.
Follow the 'data down, actions up' pattern. See the following as an example of how to implement it.
You could fire an action on the card component that changes a property on your controller. Then listen to that property into the original component so it can update.
original-component.js
reset: computed('transactionComplete', function() {
// cleanup stuff here...
})
original-component-template.hbs
{{#if transactionComplete}}
{{! stuff to show when transaction is complete... }}
{{/if}}
controller.js
transactionComplete: false,
actions: {
completeTransaction() {
this.toggleProperty('transactionComplete');
}
}
controller-template.hbs
{{original-component
transactionComplete=transactionComplete
}}
{{cart-component
transactionComplete=(action 'completeTransaction')
}}
cart-component.js
actions: {
processTransaction() {
// cleanup stuff here...
this.attrs.transactionComplete();
}
}
There might be different – and better – ways of doing this, but it depends on exactly what you need to do when resetting the original component.
Also, separately, have you considered using ember data and routing for loading the cards?
From what I've read the pattern is the Components pass data to the Actions which Pass to the Store whose value changes trigger updates in Components that subscribe to the Stores. My question is how to "react" to these triggered updates in the form of a notification? ( ie a successfully saved notification )
Ie do I add logic to the render of this notification component that only displays itself if there is a some flag attribute in the object that its subscribed to? Then deletes itself after a time. This sounds wrong.
UPDATE
Thanks to Hannes Johansson I think I have a better grasp of a pattern. What I have working is the following:
Component passes data through action to the Store
The Store interacts with the api and adds a flag to the model that the component is now notified of an updated model.
createItem: function (item) {
$.ajax({
url: '/items',
method: 'POST',
data: item,
success: function (item) {
CurrentBrandActions.addCampaign(item);
this.item = item;
item.newlyCreated = true;
this.trigger(item);
}.bind(this)
})
}
The Component sees the flag and renders a "Notification Child Component"
var newlyCreated = this.state.item.newlyCreated === true;
if (newlyCreated) {
newlyCreated = <ItemCreatedNotification item={this.state.item} />
} else {
newlyCreated = '';
}
return (
<form onSubmit={this.createItem} className="form">
{newlyCreated}
Something needs to move the app to a new place based on this event. Should this be a) the Notification Child Component b) Parent Component c) The Store?
According to Colin Megill's talk on flux api patterns the api interaction should occur in the Action, but reflux doesn't really allow for that.
UPDATE 2
Component passes data to an Action called createItemRequest
The Action has a preEmit hook that actually does the api call. The createItemRequest continues to the Store so that the store can change the model to reflect the state of sending which is then displayed in the component( maybe show a spinner ). The Action is also responsible for firing two other events depending on the api result.
ItemActions.createItemRequest.preEmit = function (data) {
$.ajax({
url: '/items',
method: 'POST',
data: data,
success: function (item) {
ItemActions.itemCreatedSuccess(item);
},
error: function (error) {
ItemActions.itemCreatedError(error);
}
});
}
There are different approaches to this. For example, in Reflux it's very easy to listen directly to actions if you choose to, since each action is actually a "dispatcher".
However, the general, purist Flux principle is that only stores register with the dispatcher and that components only listen to store updates. And the store just trigger an event that notifies that something has changed, not providing any payload. Then it's up to the component to read the store's state and determine how to render it.
One approach would be the one you describe, put some flag on the items in the store to signal that an update has happened, but it would violate the Flux principle if the components themselves then update the stored items' flags, since only stores are meant to mutate state, and only in response to actions and not from any other source. So in that case the "Flux thing" to do would probably be to trigger yet another event that signals that the newly added item has been noted so that the store can then reset the flag in response to that action.
Another approach I can think of would be to diff the state in your component when it gets notified of a store update. Then keep the flags only in the component, or even keeping newly added items in a separate list in the state and render them separately.
There are no hard rules here, except that if you want to follow the core Flux principle, components should never directly mutate stores' state, because that should only be mutated by the stores themselves in response to actions. That allows for a uni-directional data flow and a single source of truth about the data, which is the main goal of Flux.