Re-Render only a particular child - javascript

I am building a dynamic form and as of now, my component hierarchy is as follows:-
App
Caseform
DynamicFormBuilder
Context.Provider
Patients
Patient key = "patient_1"
ComponentCreator key = "patient_1_1"
Textbox
ComponentCreator key = "patient_1_2"
Textbox
ComponentCreator key = "patient_1_3"
Textbox
Patient key = "patient_2"
ComponentCreator key = "patient_2_1"
Textbox
ComponentCreator key = "patient_2_2"
Textbox
ComponentCreator key = "patient_2_3"
Textbox
As of now, I have hardcoded JSON data in caseform but eventually, it will come from the fetch call. As soon as DynamicFormBuilder receives the caseform metadata, it creates states out of it.
I am maintaining state at caseform level even for its child. I thought of doing it that way because the requirement was to support patch save(send only changed data to the backend on save button press). If anybody knows of a better way of doing this, please let me know.
I am passing in a function using context API to child components so that they can update the state in DynamicFormBuiler.
The issue I am facing is that let's say even if the user edits one textbox, the whole Dynamic form gets rendered.
I have gone through a lot of answers on StackOverflow that advise on using the shouldComponentUpdate lifecycle method, but I am not able to figure out how I will use it here.
I am adding a link to the sandbox and in the console, you can see that if a user edits a field all the things are rendered again.
CodeSandbox Link
Expected Behavior:- What I am looking for is for example:- if user-edited only one textbox say in patient 1 then only that textbox gets re-rendered again
This is my first time using react. I apologize in advance if someone feels I have not done enough research, I have read through a lot of questions but still facing some challenges, any help will be really appreciated.

If you wrap each component in the React.memo() HOC, this should prevent excessive rerenders.
https://reactjs.org/docs/react-api.html#reactmemo
-Edit-
Yes it works with class components too.
e.g.
const MyComponent = React.memo(class extends React.Component {
render () {
return <h1>TEST</h1>
}
});

Related

How do I show an activity indicator every time my flatlist data changes?

I'm setting the data that my flatlist component displays using a state called selectedStream. selectedStream changes every time the user presses a different group option. I've noticed that the flatlist takes 1-3 seconds to refresh all the posts that it's currently displaying already. I want there to be a loading indicator so that by the time the indicator goes away, the list is already properly displayed with the newly updated data.
<FlatList
maxToRenderPerBatch={5}
bounces={false}
windowSize={5}
ref={feedRef}
data={selectedStream}/>
Whenever we are working with anything related to the UI, sometimes we may face delays in UI re-rendering. However, we need to first figure out what is actually causing the delay.
The right question to ask about your code would be:
Is the rendering of items taking longer than expected? Or, is the data being passed with a delay because it is dependant on an API call or any other async task?
Once you answer that question, you may end up with two scenarios:
1. FlatList taking longer to render views
This doesn't usually happen as the RN FlatList will only render views that are visible to the user at any given time and will keep rendering new views as the user scrolls through the list. However, there may be some flickering issues for which you can refer to the below article:
8 Ways to optimise your RN FlatList
2. Passing the data causes the delay
This is the most common scenario, where we may call an API endpoint and get some data and then do setState to update any view/list accordingly. A general approach is to show some sort of a progress-bar that would indicate that the application is busy and thus maintaining a proper user-experience. The easiest way to do that is by conditional rendering.
A general example would be:
const [myList, setMyList] = useState();
function callAPIforMyList(){
// logic goes here
}
return {
{myList ? <ActivityIndicator .../> : <Flatlist .... />
}
The above code will check if myList is undefined or has a value. If undefined, it will render the ActivityIndicator or else the FlatList.
Another scenario could be when myList may have existing data but you need to update/replace it with new data. This way the above check may fail, so we can put another check:
const [myList, setMyList] = useState();
const [isAPIbusy, setAPIBusy] = useState(false)
function callAPIformyList() {
setAPIBusy(true)
/// other logics or async calls or redux-dispatch
setAPIBusy(false)
}
return {
{!isAPIBusy && myList ? (<Flatlist .... />) : (<ActivityIndicator .../>)
}
You can add multiple conditions using more turneries such as isAPIBusy ? <View1> : otherBoolean ? <View2> : <Default_View_When_No_Conditions_Match)/>
Hope this helps clarify your needs.

How to control state of multiple inputs in react

I'm creating an app wherein a user can create a set of flashcards with a term and description each. I also want the flashcards to be editable as soon as they add a flashcard. So each flashcard they add gets pushed to a variable with a controlled state. Then I created a component that returns inputs with all of the flashcard data.
this is what it would like look like:
There's no problem with that it works fine. But I want them to be editable once added. So I added an onchange to each of the inputs and did this:
function handleChange(event){
const {value, name, dataset} = event.target
const index = dataset.index
const newFlashcardSet = [...flashcardValues]
newFlashcardSet[index][name] = value;
setFlashcardValues(newFlashcardSet)
}
so when the user tries to type on the submitted flashcard they can edit it.
PROBLEM
As you can probably tell, each key keystroke calls the function, and rerenders all the data. Causing it to eat up a lot of cpu usage, even though it is not noticeable at first, but as soon as you add more flashcards, you would definetly notice the slow performance, because it rerenders all the data every keystroke. What is the best way to go around this?

React JS how to remember the clicked item in a list when I pass to next page

I listed some countries in my Country.js page using ListItem. I created a CodeSandbox and this is the link for it:
My Code
I want the program to remember the clicked list item in the list in Country.js file and when I go to next page (press the next button) and back again (press the back button), the selected item before going to next page should also be selected. Sorry for the bad stylings, I did not add them.
Note: I implemented this feature in forms (radio group) but, I could not do this in a list.
The problem is that your state is currently at the level of your CountryList component so every time this component is unmounted, its state disappear.
What you need to do is lift up the state of the component in your UserForm component so it won't disappear when you change page
there is a lot solution for this case (holds data in):
a) context,
b) redux - redux persist - holds in persistence
c) sessionStorage
Of course, the state can be transferred between the components (but only one way - there is no two way data binding as in angular)
As context and redux are more global concepts - I suggest you save the data at each step to sessionStorage and finally collect that data and send it to API or whatever.
// Save data to sessionStorage
sessionStorage.setItem('Country', countryName);
sessionStorage.setItem('FilmSurvey', surveyName);
// Get saved data from sessionStorage
var dataCountry = sessionStorage.getItem('Country');
var dataSurvey = sessionStorage.getItem('FilmSurvey');
Then, when it works,that you refactor it somehow.
And general advice - I recommend using Hooks - they are more consistent and newer

How can Aurelia custom elements interact?

In Aurelia, I have a parent component that is composed of several other components. To keep this example simple, say that one component is a State dropdown, and another component is a City dropdown. There will be other components that depend on the selected city, but those two are enough to illustrate the issue. The parent view looks like this:
<template>
<compose view-model="StatePicker"></compose>
<compose view-model="CityPicker"></compose>
</template>
The idea is that you would pick a state from the StatePicker, which would then populate the CityPicker, and you would then pick a city which would populate other components. The routes would look like /:state/:city, with both being optional. If :state is omitted, then the url will automatically redirect to the first available state.
I'm using the EventAggregator to send messages between the components (the parent sends a message to the CityPicker when a state is selected). This is working, except for the initial load of the application. The parent is sending the message to the CityPicker, but the CityPicker component hasn't been activated yet, and it never receives the message.
Here's a Plunker that shows the problem. You can see that the city dropdown is initally empty, but it starts working once you change the state dropdown. Watch the console for logging messages.
So the question is: Is there a way to know that all the components are loaded before I start sending messages around? Is there a better technique that I should be using? Right now, the StatePicker sends a message to the parent that the state has changed, and then the parent sends a message to the CityPicker that the state has changed. That seems a little roundabout, but it's possible that the user could enter an invalid state in the url, and I liked the idea of being able to validate the state in one place (the parent) before all the various other components try to load data based on it.
The view/viewModel Pattern
You would want your custom elements to drive data in your viewModel (or in Angular / MVC language, controller). The viewModel captures information about the current state of the page. So for example, you could have a addressViewModel route that has state and city properties. Then, you could hook up your custom elements to drive data into those variables. Likewise, they could listen to information on those variables.
Here's an example of something you might write:
address.html
<state-picker stateList.one-way="stateList" value.bind="state" change.delegate="updateCities()"></state-picker>
<city-picker cityList.one-way="cityList" value.bind="city"></city-picker>
address.js
class AddressViewModel {
state = null;
city = null;
stateList = ['Alabama', 'Alaska', 'Some others', 'Wyoming'];
cityList = [];
updateCities() {
let state = this.state;
http.get(`API/cities?state=${state}`) // get http module through dependency injection
.then((response) => {
var cities = response.content;
this.cities.length = 0; // remove current entries
Array.prototype.push.apply(this.cities, cities);
});
}
}
If you wanted to get a little more advanced and isolate all of your state and city logic into their respective custom elements, you might try following this design pattern:
address.html
<state-picker value.bind="state" country="US"></state-picker>
<city-picker value.bind="city" state.bind="state"></city-picker>
address.js
class cityPickerViewModel {
#bindable
state = null;
cities = [];
constructor() {
// set up subscription that listens for state changes and calls the update
// cities function, see the aurelia documentation on the BindingEngine or this
// StackOverflow question:
// http://stackoverflow.com/questions/28419242/property-change-subscription-with-aurelia
}
updateCities() {
/// same as before
}
}
The EventAggregator Pattern
In this case, you would not want to use the EventAggregator. The EventAggregator is best used for collecting various messages from disparate places in one central location. For example, if you had a module that collected app notifications in one notification panel. In this case, the notification panel has no idea who might be sending messages to it, so it would just collect all messages of a particular type; likewise, any component could send messages whether or not there is a notification panel enabled.

Dynamic defaultValue on Combobox React Widgets

I'm fairly new to javascript and I'm currently making a web client that display some data a database. Pretty standard stuff I guess. I use react.js and I installed react-widgets to get some nice widgets. The comboxbox I use is getting it's data from a mongo database depending on the collection chosen from another comboxbox. So by changing the collection, the data inside is updated to reflect the change. This works well, but I can't seem to be able to set the default value. Here the code to render the comboxbox:
<Combobox onChange= {this.handleOnChange} data = {this.state.myValues} defaultValue= {this.state.myValues[0]}/>
I have had this issue with a couple of widgets that does not seem to be able to get updated dynamically. They work if all the data is there the first time I'm rendering (static data for example), but they don't if the data is empty at first and gets populated afterward. *I did make sure I call setState to call the render function.
Did I miss something? Is it a javascript issue that I don't understand?
Thanks!
Try wrapping the Combobox with a condition.
Something like this
{ this.state.myValues[0] &&
YOUR_TAG_COMBO
<Combobox onChange= {this.handleOnChange} data = {this.state.myValues} defaultValue= {this.state.myValues[0]}/>
}
I think you might be better off using the value prop, instead of defaultValue, it will give you more control over the component, and it will repopulate the form element when the state has changed.

Categories