Manipulating parent's array prop - Reactjs - javascript

This question may sound a little complicated. But i am stuck here, so i am throwing it here.
I am rendering a child component multiple times in parent with different properties and an array.
Parent contains an array of headings, i am passing it to child components and mapping select options with that array. If an heading is selected from any of the child components, that heading should display as disabled in other child components.
Child:
{
this.props.xarray.map((heading, index) => {
if (heading.headingis.toLowerCase().indexOf(this.state.field) != -1) {
this.setcheckstate(); //ignore this function
this.props.actionis(index, 'disabled'); //Using this function (passed from parent) to disable selected option
return <option value={heading.headingis} key={index} selected disabled={heading.disabled}>{heading.headingis}</option>
}else{
return <option value={heading.headingis} key={index} disabled={heading.disabled}>{heading.headingis}</option>
}
})
}
Parent's actionis function:
handlerHeading(index, disabled) {
xarray[index]['disabled'] = disabled;
}
It's working somehow BUT problem is
If first component is rendered, it will disable that value
Then 2nd component is rendered, it will disable that value
along with previous one.
and so on...
But in first component, only one value is disabled, in 2nd component 2 values are disabled and in 3rd component 3 values are disabled. And so on...
But i want if one value is disabled in component 6, it should get disabled in all previous components.
Have a look at following images for example.
Component #1 Rendered:
Component #4 Rendered:

I have resolved this, by changing xarray to state of parent.
This:
state = {xarray: xarray}
And:
handlerHeading(index, disabled) {
xarray[index]['disabled'] = disabled;
this.setState({ xarray: xarray });
}
For some reason you cannot change Props as you desire. See This Question

Related

How to update react component props (and rerender) from "outside React"

My application is mixed with vanilla JS and React components, and I have a component that I would like to rerender by changing the props from outside the react component. How would I accomplish this? And maybe I need to be using "state" instead?
This component is only shown when a user clicks a dropdown link in a list of items. Because the list can be quite long, I am trying to be efficient with creating only one component and then changing the props (rerendering) depending on which item in the list if clicked.
This is the code the initially creates the component with references to the "root" and "element"
// Render AdjustDateFormulaComponent
let rootContainer = document.getElementById('rc_dateFormulaComponent');
if (rootContainer) {
this.dataFormulaWidgetRoot = createRoot(rootContainer);
this.dataFormulaWidgetElement = createElement(AdjustDateFormulaComponent, {
id: null,
entityType: 'taskTemplateEntry',
onSave: _this._dateFormulaUpdated, // callback method
});
this.dataFormulaWidgetRoot.render(this.dataFormulaWidgetElement);
}
When item is clicked, rerender by changing props (specifically the "id" field) and show/move in the appropriate dropdown div
_this.$widgetDiv.find('.dueDateAdjustDropdown').on('show.bs.dropdown', function () {
// Change props/rerender here with correct id
this.dataFormulaWidgetElement.props = { id: 124 }; // <--- THIS DOESNT WORK OBVIOUSLY
// When dropdown shown, move react component into dropdown div
$(document.getElementById('rc_dateFormulaComponent'))
.detach()
.appendTo($(this).find('.dropdownContent'));
});
Many thanks in advance.

Angular change child component template with parent component action

I'm trying to create a child component (it's a form), which appears within a parent component in a modal.
The form (child component) must change according to the type chosen in the parent through several buttons (each button is a different type).
When you click on the button, the child component must modify the form according to the type chosen in the parent. The form controls change properly, but the template is not modified. The code is the following:
Parent method that opens the modal and calls the child component
show(value: string){
this.type = value;
this.entityFormComponent.type = this.type;
this.entityFormComponent.ngOnInit();
// Set submitAttemp to false for the new form
this.entityFormComponent.submitAttemp = false;
this.contentModal.show();
}
Method ngOnInit of the child component
ngOnInit(): void {
if(this.creating){
this.entity = new Entity();
} else {
this.entityService.get(this.nameToEdit, this.type)
}
// Initialize the form and add the corresponding attributes according to the type
this.entityForm = new FormGroup({});
this.entityHelper.getEntityFormAttributes(this.entityForm, this.type, this.entity);
}
So far it seems that everything is correct and the form group that is generated is adequate every time you click on the parent button, but the inputs of the form do not appear. They are generated through an auxiliary method, which returns true or false through the type and attribute of the input that is passed:
<div class="col-md-6" *ngIf="entityHelper.getEntityInputsAttributes('shadow', type)">
The type in the view is "undefined", but in the component appears correctly. Why doesn't the view update the type correctly? What do I have to do to update it?
For parent child component interactions, it is advised to use the Input directive.
If you have different types of forms you could use an enum. And bind each enum entry to each of the buttons by adding an action to the button that sets a property with its correspondent type. For example:
onClick(type: FormTypes) {
this.formType = type;
}
You parent template would look something like
<child-component [typeOfForm]=formType </child-component>
Your child component would need to execute changes when a new formType is provided therefore you would need to do something like
#Input()
set typeOfForm(typeOfForm: FormType) {
//make the necessary changes on form type change.
}
Also to avoid manually executing the ngOnInit function would can surround your child component tag with a div and have it showing or not with a boolean property on the typescript code like so:
<div *nfIf="showForm">
<child-component [typeOfForm]=formType </child-component>
</div>
doing this your show function would only set the showForm property to true.
Hope this helps, for more detailed information on component interaction I suggest you check out this documentation Angular Component interaction

Material-UI Nested Checkbox selection doesn't render change to parent in DOM

I have a working nested checkbox example I am working on implementing into my application. The logic for the checkboxs is functional enough to cover theses 7 cases
- Scenario - No children, no parent selected
- Select the parent -> select the parent and all children
- Select a child -> select that child and its parent
- Scenario - All children and the parent selected
- Select the parent -> deselect the parent and all children
- Select a child -> deselect only that child
- Scenario - Some children and the parent selected
- Select the parent -> select all unselected children
- Select a child -> select that child, parent remains selected
- Deselect last child -> deselect the child and deselect the parent
The issue I am running into is that in scenarios when the child is selected or deselected, the parent checkbox's state is updated, but the actual visual change of the checkbox isn't happening.
From my code:
<Checkbox
disableRipple
edge="start"
checked={sub.checked}
onChange={() =>
this.handleCheckClick(sub.id, parentIndex)
}
/>
The Material-UI <Checkbox /> component is inside of a list of items, and each item has a subsequent list of items with their own <Checkbox /> components respectively. When I select the parent checkbox of the list the state changes properly through that handleCheckClick() method, and the state is updated properly, and the visual update happens as you would think.
When the child is clicked, an ideal situation would be that the parent is then selected, which it is in the react component's state, but the visual aspect of the change isn't taking place despite the checkbox's checked state being mapped to state.
The interesting part is that if I use a native input like this:
<input
type="checkbox"
disableRipple
edge="start"
checked={sub.checked}
onChange={() =>
this.handleCheckClick(sub.id, parentIndex)
}
/>
The correct behavior is both displayed visually, but also in state (like it already was). I am not sure if this is an error specific to Material-UI, or a race condition, or something else happening, but I do know that the logic in the handleCheckClick() function is sound as changes are being displayed properly in the react state.
You actually have a warning indicating what is going on:
Warning: A component is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/docs/forms.html#controlled-components
The issue is that you are not initializing the checked state in createSubscriptions (see code below), so initially sub.checked is undefined. This causes Material-UI to treat the checkbox as uncontrolled, so specifying the checked state later has no effect.
function createSubscriptions() {
const statusSet = ["Processing", "Completed", "Submitted", "Error"];
const quantity = (faker.random.number() % 10) + 1;
const subscriptions = [];
let x = 0;
while (x < quantity) {
subscriptions.push({
id: faker.random.uuid(),
name: faker.lorem.words(),
description: faker.lorem.sentence(),
created: faker.date.past(),
status: statusSet[faker.random.number() % 4],
checked: false, // ADDING THIS FIXES IT
geoJson: {
features: createGeoJson()
}
});
x += 1;
}
return subscriptions;
}
Alternatively, you can handle this at the point of rendering the Checkbox:
<Checkbox
disableRipple
edge="start"
checked={sub.checked || false}
onChange={() =>
this.handleCheckClick(sub.id, parentIndex)
}
/>

Semantic UI React, Dropdown Selection

Please take a look at my code for Dropdown,
I'm using the semantic ui react dropdown on an EditProfile component. I have pasted a sample code here, https://codesandbox.io/s/m4288nx4z8, but I could not get it to work because I'm not very familiar with functional components in React, I've always used Class component. But you can check the full code for the whole component below in the github gist.
https://gist.github.com/mayordwells/b0cbb7b63af85269091f1f98296fd9bb
Please, I need help on inserting values from multiple select options of a Dropdown into the Database and also a way to display that back upon viewing the profile edit page again.
I'm using semantic-ui-react in react + rails app.
Also when I insert a value using a normal drop down without multiple select, the value gets persisted into the database.
<Dropdown
placeholder='Select Country'
fluid
search
selection
options={countryOptions}
name='country'
defaultValue={this.state.extraInfo.country}
onChange={(e) => this.handleExtraInfoChange('country', e)}
/>
This code handles change for the dropdown elements.
handleExtraInfoChange = (name, event) => {
let value;
if (event.target.value !== undefined) {
value = event.target.value;
} else {
value = event.target.textContent;
}
let newExtraInfo = Object.assign(this.state.extraInfo, { [name]: value })
this.setState({ extraInfo: newExtraInfo});
}
But when I visit the page again, I get a white blank in the input box. Here's a screen pic for that. When I comment out the defaultValue or value property(i have tried with defaultValue and value), the white blank disappears, but the value picked by a user is also not seen.
Please advice what is a possible solution to this misbehavior? And what is the best way to insert multiple values into the Database?
Thanks in advance for your time.
A functional component does not have state, it's used for composition; you want to store state, so you either have to create a Component class or you need an external state container like redux.

React Input Element Not Updating When Updating Component

I'm trying to make a React version of this.
You can see what I have so far on this Codepen.
The issue here is that when you type into a text field (under the "Suspected Player" column) and then click the adjacent name (under the "Role" column) to remove that row, the text persists inside the input, and is then adjacent to a different name.
The expected behavior is that you'd click the name to remove the row and both the elements (the name and text input) would disappear.
All the items in the table are stored in an array called checklist in the state of the RoleList class.
The component for each list item in the table is this:
class RoleCheckpoint extends React.Component {
constructor() {
super();
this.state = {
text: ""
};
}
deleteThis() {
this.props.removeRole(this.props.role.id);
}
onChange(e) {
this.setState({
text: e.target.value
});
}
render() {
console.log(this.props);
return (
<tr>
<td className="vertAlign remove noselect" onClick={this.deleteThis.bind(this)}>
{this.props.role.el}
</td>
<td>
<input type="text" className="form-control" spellCheck="false" onChange={this.onChange.bind(this)} value={this.state.text} />
</td>
</tr>
);
}
}
When you click on the name it invokes the components deleteThis function, which in turn invokes RoleList's removeRole function (that goes through the array of list items, and removes the one with the matching ID from the components state), which is being passed as a prop to RoleCheckpoint.
I've tried using this.forceUpdate() in the deleteThis function, and as you can see I've tried binding the input field's value to the components state (called text), yet still the input doesn't update when removing the component from the DOM.
Any help is appreciated, if you need more clarification I'll be happy to provide it.
A "key" is a special string attribute you need to include when creating lists of elements.
<RoleCheckpoint role={role} key={role.id} removeRole={this.removeRole.bind(this)} />
The behaviour is typical when you have a list of components and you have not set the key of the objects correctly. It need to be something that identifies the object.
Probably you can use the name field:
<RoleCheckpoint key={role.name} .../>
See https://facebook.github.io/react/docs/lists-and-keys.html

Categories