React changes not visible in DOM - javascript

I have a good history playing with react. But today I faced a very strange issue.
I have a hamburger which receives a list of values and the one of the values is selected which I send from the parent through this.refs.child1.setValues(). this setValues function calls setState and that one value is selected through className parameter in react.
So in the first step I select first value among 20 values.
the react tab shows this about the first li element:
className="{something} +class1"
rest all elements have:
className="{something}"
which is fine. ('class1' is the class which is added dynamically on setstate) and the dom shows first value selected and the rest unselected.
now problem is in the second step as I select another value say 3rd one, so first value should get deselected and 3rd should be selected and the expected react values for 3rd should be
className="{something} + class1" and for the rest its showing className="{something}"
which is working fine in the react tab BUT in the elements tab both the li elements are getting selected as they both have class1 but class1 should be only with the 3rd element not with the first one (which was previously selected)

Since React works by using the Virtual DOM, it only makes changes in the DOM to elements it detects a change to. So, since you're never actually removing a class from the first element, it never does a rerender of it, it only rerenders the third one because that's the only one you actually changed.
You need to do something like this:
removeClass() {
let allLinks = document.querySelectorAll('.something');
allLinks.map((link) => link.classList.remove('class1'));
//and here just call the function to add the class to the right
//element.
}

Related

How Do I Target A Specific Node In React Component and Have Independent Behavior When Component is Reused?

I am trying to target a specific node in my react reusable component and perform an operation on it. But I want to be able to do same thing several times independently because I will be returning this my reusable component several times on the same page on my App.js. How do I target that div node uniquely?
Here is my situation:
I created a react class component (I am not using function component for this project). This component contains two divs and a button. The first div is a red box. Under it is the second div which is empty with the class "result". There is also a button. When I click this button, the package Html2Canvas will convert the first div into an image and append it to the second div.
Now from the Html2Canvas documentation, they only specified how to append the generated canvas to the body of the DOM:
html2canvas(document.querySelector("#capture")).then(canvas => {
document.body.appendChild(canvas)
});
Since I needed to append to that empty second div, I did:
html2canvas(document.querySelector("#capture")).then(canvas => {
document.queryselector('.result').appendChild(canvas)
});
This would have been fine if I am just using this component once on a page, but in my App.js, I am actually returning this my component multiple times, so what now happens is that each time I click that button, it targets only the first instance of the empty whereas what I need is that each occurrence of my component should behave like the first one independently.
Yes I have tried using CreateRef() to target the current occurrence oof the empty dive but it does not work. It sees the ref as undefined when I call it inside the Html2Caanvas function. But outside of it, it is defined. Ok I tried creating the ref inside, still gave an error of undefined.
I understand that this whole thing I have written may be somewhat vague, so here is a CodeSandBox example of what I want.
When you open it and click on the first "Finish" button, you will see that the red box gets converted to an image, and the image is added under the red box, that is, it is appended to the div with class "result". But when you click on the second "Finish" button, instead of the generated image of the blue box coming under the blue box div, it goes under or beside the previous image. The expected behavior is for it to appear under the blue box. And if I replicate that component "ReUsable" as many times as I want in the App.js, the same behavior is expected. Independence.
I know this is a lot. I look forward to and appreciate your solutions.
First, the ref approach was great (the one you commented out), only that you were referencing the containerRef and not the screenshotRef.
the main issue came from the callback function passed to html2canvas, even though you called bind on handleFinish, binding "this" context to the current class context, the execution context of "this" in the callback is undefined because you used a regular function and regular function always refers to the context of the function being called(html2canvas).
arrow functions on the other hand treat the "this" keyword differently. They don't define their own context.
kindly update your handleFinish method to this
handleFinish(event) {
html2canvas(this.containerRef.current, 1).then((canvas) =>{
this.screenshotRef.current.append(canvas);
// document.querySelector(".result").append(canvas);
});
}

Run function on load on .map element in React

I have a set of components being rendered from a .map() function in my React application.
I have a function that fires on element's onClick which works great. However, I also need to fire this function automatically on a specific element when the page loads.
Here's a rather rudimentary example: https://codesandbox.io/s/ecstatic-cartwright-ve84yt.
So in the example, I have an array of 5 names, when you click the card, the field at the bottom displays the name of the selected card.
What I'm trying is to pre-select (Without using setState's initial value) one of the cards.
If the selectedName is not initialize, select first name in the list.
Selected Name: {this.state.selectedName || names[0]}
https://codesandbox.io/s/little-hill-wv4200
If you want to preselect. You have to use componentDidMount lifecycle react method. It will be fired after your component will be rendered.
componentDidMount() {
this.setState({
selectedName: names[1]
});
}

Unexpected select dropdown behaviour with vue.js

If I remove background-green class binding or value binding from the select element then it works as a normal dropdown. But let's say I didn't remove the background-green class binding then after selecting, the selected option is not displayed but if I select again the same selected option or other option then it would be displayed. Same behavior with value binding. So why it's working second time? This is my real concern/confusion.
Here is the link of running example with sample code.
The problem is a mixture of :value="defaultValue" not actually changing (because defaultValue never changes) and also a side effect of a rerender happening the first time.
Since :value is essentially always "", any time the <select> is re-rendered, Vue sets the value to that empty string.
You have handleInput set fillBg = true which triggers a re-render, because background-green was not in the initial render. This means Vue will reset the value of the <select> back to defaultValue (blank)
During the time it appears to work after the first selection, what's actually happening is DOM local state showing what you selected. Since there is no change to the vdom (background-green is already there), Vue is not re-rendering and thus not resetting the value.
The proper way to fix this is to either update defaultValue (perhaps rename this) during the input event or use v-model. The point is to have Vue set the proper value any time it renders.
handleInput(e) {
this.fillBg = true;
this.defaultValue = e.target.value;
},

Polymer 2 iron-selector selected element doesn't update after the dom changes order of elements

I have a component, which has 4 instances.
User clicks number 2, product B gets selected.
When the dom is re-ordered, later after he selects another option, these 4 instances of the component card-brick are re-ordered (because price changes, so this happens automatically in the flow) all good... except for the fact that the selected element, which was number 2 , moved to position 1.
and... the 2nd element continues to be selected instead of product B!
The ui shows selected product D.
it seems that somehow polymer 2 is ignoring this selected attribute when repainting the DOM. how can I avoid this?
<iron-selector id="something-semantic" attr-for-selected="value" selected={{value}}>
<template is="dom-repeat" items="[[filteredProductosCategorias]]" as="productoCategoria" id="productoTipo">
<div class="card-brick" value="[[productCategoria.id]]" tabindex="1" on-keyup="_seleccionaCategoria">
</div>
</template>
</iron-selector>
the selection is stick to previously selected element in the DOM order. how can I make this selected value to also update don dom changes??
any suggestion to overcome this problem?? users can be confused.
The problem is that dom-repeat doesn't rebuild the elements inside, because that would be a heavy operation. It just assigns new values to child elements, meaning that iron-selector has no clue that the content has changed. In the example, you selected the second element, and after the re-sort, it's still the second element selected.
I'm confused in what _selectCategory does, because if it sets the category, then you probably don't even need iron-selector. I never used that element myself (and would never use it, because it's simple to write it myself). Anyway, if you re-sort using _selectCategory, then just set the new value property with this.set().

jQuery clone misunderstanding

I'm not understanding this cloning process... this is what's happening, there are four pictures, three clicks, first photo is the initial state.
In the third picture there are two boxes, that's fine, but rather than each box having one project name input and add task button, there are two in the first box, and normal in the second box. Click the button again and it becomes 3:2:1, next click it would be 4:3:2:1, etc... I don't want that. I just want boxes to be added with one piece per box.
code
function addProject() {
$(project).clone().appendTo(".projectPanel");
$(projectNameInput).clone().appendTo(".project");
$(addTaskButton).clone().appendTo(".project");
}
Your problem is your appendTo it appends the element to all elements in the set of matched elements so everything with the class "project". for more information on it try looking at the jquery appendTo documentation. to fix it try something like this
function addProject() {
var newProject=$(project).clone();
newProject.appendTo(".projectPanel");
$(projectNameInput).clone().appendTo(newProject);
$(addTaskButton).clone().appendTo(newProject);
}
using the return value of $(project).clone() allows you to grab only the new project rather than all of the projects that currently exist

Categories