How remove an element from array with Redux ? JavaScript - javascript

i wanted to remove an element from an array but i'm not getting, my code is:
const renderCount = state => {
const peopleHtml = state.filteredPeople.map(person => {
const personHtml = document.createElement('LI');
const personName = document.createTextNode(person.name);
const buttonDelete = document.createElement('Button');
const textOfButtonDelete = document.createTextNode('Delete');
buttonDelete.appendChild(textOfButtonDelete);
personHtml.appendChild(personName);
personHtml.appendChild(buttonDelete);
buttonDelete.onclick = function() {
return {...state,filteredPeople : state.filteredPeople.filter( (item, index) => index !=="Jean")}
}
return personHtml;
});
resultado.innerHTML = '';
peopleHtml.forEach(personHtml => resultado.appendChild(personHtml));
};
export default renderCount;
What the code makes?
He renders the elements of an array, 3 in 3. Each element of array have a button 'delete'and each time that i clicked that, a element get out off the screen.
The code and the button are: buttonDelete.onclick.....
Thanks and good afternoon.

This is not the right way to do in reactjs. React believe on Virtual DOM. Your states values and HTML elements will be talking with each other and you do not need to use appendChild or removeChild to update them , just update the state value. Some thing like below
render()
{
return (
<div>
<ul>
{this.state.filteredPeople.map(person => { =>
<li>
{person.name}
<button onClick={this.deleteme.bind(this,person.id)}>Delete</button>
</li>
)}
</ul>
</div>
);
}
deleteme(index){
let initial_val = this.state.contents.slice();
let obj_to_del= initial_val.find(person => person.id === id);
initial_val.remove(obj_to_del);
this.setState({people:initial_val});
}

Related

How can I pass data from a React component to main file?

I am having trouble passing data from component to main file in react. It is a basic quiz application, get quiz objects from api and pass data to the "Question" component. In the main file i have score state to decide how many answers are correct. But when answer button clicked application see value in the component. I want to match answer value and correct answer and increase score by state. Here is the Question component and main file.
export default function Question(props) {
const [flag,setFlag] = React.useState(true)
// TOGGLE BUTTON BACKGROUND COLOR WHEN BTN CLICKED
function toggleBtnBgColor(btn) {
if(flag) {
btn.target.classList.toggle("dark-bg-color")
setFlag(false)
}
}
return (
<>
<div className="question" key={props.index}>{props.question}</div>
{props.answers.map((answer,index)=> {
return (
<button key={index} onClick={toggleBtnBgColor} className="answer-button">{answer}</button>
)
})}
<hr></hr>
</>
)
}
Main file;
const [data, setData] = React.useState([])
const [score, setScore] = React.useState(0)
const [startAgain, setStartAgain] = React.useState(false)
// FETCH QUIZ DATA FROM API
const fetchData = async () => {
await fetch("https://opentdb.com/api.php?amount=5&type=multiple")
.then((response) => response.json())
.then((data) => setData(data.results))
}
React.useEffect(()=> {
fetchData()
},[])
// SHUFFLE ALGORITHM TO USE LATER
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle.
while (currentIndex != 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
// RENDER QUESTIONS
const questionElements = data.map((question, index) => {
// ANSWERS ARRAY AND SHUFFLE
let answers = [...question.incorrect_answers,question.correct_answer]
shuffle(answers)
return (
<Question key={index} index={index} question={question.question} answers={answers}/>
)
})
// CALCULATE FINAL SCORE WHEN CHECK BTN CLICKED
function calcScore() {
for(let question of questionElements) {
console.log(question.calcScore)
}
setStartAgain(true)
}
return (
<div className="question-container">
{questionElements}
<p>{startAgain && ("Your score is "+ score +"/5")}</p>
<button onClick={calcScore} className="check-button">{!startAgain ? "Check Answers" : "Restart Quiz"}</button>
</div>
)
Check this out, we pass a function to Question that receives the answer and index. The parent will update an array that carries all the answers
https://codesandbox.io/s/black-cdn-p6lo5v?file=/src/App.js
const setAnswer = (index, answer) => {
const newAnswers = [...answers];
newAnswers[index] = answer;
setAnswers(newAnswers);
};
// RENDER QUESTIONS
const questionElements = data.map((question, index) => {
// ANSWERS ARRAY AND SHUFFLE
let answers = [...question.incorrect_answers, question.correct_answer];
shuffle(answers);
return (
<Question
key={index}
index={index}
question={question.question}
answers={answers}
setAnswer={setAnswer}
/>
);
});
If I am not wrong I understand that you want to update parent component's state. You can pass setState function as props to child component like this:
return (
<Question key={index} index={index}
question={question.question}
answers={answers}
updateScore ={setScore}/>
)
You can make use of props for the same and pass setScore hook to set the values.
You can make use of Redux and get/set the values to/from it.
Most people prefer redux over passing the setState hooks to children as props, however many times people pass the hook as props and solve this.
Hope this helps you!

Is there a way to simplify this snippet?

const navTitle = document.querySelectorAll(".nav-sec");
const subTitle = document.querySelector(".subtitle");
const artTexts = document.querySelectorAll(".art-text");
const sectionTitles = document.querySelectorAll(".section_title");
const footerContent = document.querySelector(".footer_content");
const projectsTitle = document.querySelectorAll(".title-project");
const projectsDescription = document.querySelectorAll(".desc-project");
const aboutTitle = document.querySelectorAll(".about_title");
const contactContent = document.querySelector(".contact_content");
const aboutContent = document.querySelectorAll(".about_content");
btnLang.addEventListener("click", () => {
const attr = (btnEnglish.classList.contains("hiddden") ? btnPortuguese : btnEnglish).getAttribute("language");
navTitle.forEach((el, index) => (el.textContent = data[attr].navbar[index]));
sectionTitles.forEach((title, index) => (title.textContent = data[attr].navbar[index]));
projectsTitle.forEach((project_titles, index) => (project_titles.textContent = data[attr].project_titles[index]));
projectsDescription.forEach((project_description, index) => (project_description.textContent = data[attr].project_description[index]));
aboutTitle.forEach((about_title, index) => (about_title.textContent = data[attr].about_title[index]));
aboutContent.forEach((about_content, index) => (about_content.textContent = data[attr].about_content[index]));
contactContent.textContent = data[attr].contact_content;
subTitle.textContent = data[attr].subtitle;
footerContent.textContent = data[attr].footer_content;
});
I'm a benninger and I know that when you are repeating yourself too much, you can probably simplify things. But how can I approach something like this code? DRY ftw.
Assuming you have the same number of array elements in data as matching elements in the DOM, one approach is to use an array of selectors, each of which is tied to the associated property name on the data. Iterate over the selectors and language properties, then loop over each element matching the selector to assign the same index from the language property.
const selectorsByLangProp = [
['navbar', '.nav-sec'],
['navbar', '.section_title'],
['project_titles', '.title-project'],
['project_description', '.desc-project'],
['about_title', '.about_title'],
['about_content', '.about_content'],
];
btnLang.addEventListener("click", () => {
const attr = (btnEnglish.classList.contains("hiddden") ? btnPortuguese : btnEnglish).getAttribute("language");
const langData = data[attr];
for (const [langProp, selector] of selectorsByLangProp) {
document.querySelectorAll(selector).forEach((elm, i) => {
elm.textContent = langData[langProp][i];
});
}
contactContent.textContent = langData.contact_content;
subTitle.textContent = langData.subtitle;
footerContent.textContent = langData.footer_content;
});
For larger, complicated pages, a nicer approach to this sort of thing would be to construct the HTML directly from the data, instead of having separate selectors and properties. For example, with React, one might be able to do something similar to the following:
const Page = ({ langData }) => (<>
<div>{langData.subTitle}</div>
{
langData.sections.map((sectionData) => (
<section>
<div>{sectionData.project_title}</div>
<div>{sectionData.project_description}</div>
</section>
))
}
<footer>{langData.contact_content} {langData.footer_content}</footer>
</>);
(That isn't working code, but it's an example of what implementing this sort of thing could look like)

React useState not updating because of useRef

I am experiencing a very odd issue with my react code : useState isn't updating the view and after literally trying everything the issue is still there. I made a simple code to explain the issue :
function(){
const [enterJob, setEnterJob] = useState(false);
const [jobSelection, setJobSelection] = useState(Array(someList.length).fill(false));
const jobRef = useRef();
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = jobSelection;
c[n] = !c[n];
setJobSelection(c);
};
const handleMouse = (e) =>{
if (!jobRef.current.contains(e.target)){
setEnterJob(false);
};
};
useEffect(() => {
window.addEventListener("mousedown", handleMouse);
return () => window.removeEventListener("mousedown", handleMouse);
});
return(
<div ref={jobRef}>
<input onFocus={()=> setEnterJob(true)} />
<div style={{display: `${enterJob ? 'flex' : 'none'}`}} >
<ul>
{ someList.map((item,index)=>
<li id={`${index}`} onClick={handleJobClick}> {jobSelection[index] ? item : "you clicked on the button"} </li> )}
</ul>
</div>
</div>
)
}
Some explanations: I am using UseEffect and useRef to create a dropDown menu that disappears when you clic outside the container. Now when I want to clic on a value of this drop-down menu it doesn't update the DOM while I am using useState to update the value of the string responsible for the change.
Thank you in advance,
Charbel
The problem is that you are mutatiing your jobSelection instead of creating a new object. And react will skip the rerender if the the objects has the same reference as before:
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = [...jobSelection]; // Create a new array
c[n] = !c[n];
setJobSelection(c);
};
Issues
If I understand your issue then I believe it is because you are directly mutating your state.
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = jobSelection;
c[n] = !c[n]; // <-- mutation!
setJobSelection(c);
};
You are also missing react keys on the mapped list items.
Solution
Since the next state depends on the previous state you should use a functional state update to copy your state first, then update it.
I suggest:
converting handleJobClick to consume the index directly, a curried function handles this cleanly
Add a react key to the mapped list items
Code
const handleJobClick = index => () => {
setJobSelection(jobSelection => jobSelection.map(
(selection, i) => index === i ? !selection : selection // <-- toggle selection at matched index
);
};
...
<ul>
{someList.map((item, index)=> (
<li
key={index} // <-- index as react key, ok since not adding/removing/sorting jobs
onClick={handleJobClick(index)} // <-- pass index to handler
>
{jobSelection[index] ? item : "you clicked on the button"}
</li>
))}
</ul>

Mother component controlling input for child components, but doesn't rerender on change

I have a React component, where i have a category array, that i map, and index the data form another object.
const labelComponents = categories.map((category, index) =>{
const data = globalState.filter(systemLabel => systemLabel.value === category.categoryKey).pop()
return(
<Label
key={data && data.text ? data.text + category : category + index}
category={category}
data={globalState.filter(systemLabel => systemLabel.value === category.categoryKey).pop()}
deleteLabel={deleteLabel}
updateLabelValue={updateLabelValue}
/>
)
})
I pass in the function updateLabelValue where i try to update the specefic text attribute on the chosen object.
This function could probably be refactored, but it works for now.
const updateLabelValue = (categoryKey, value) =>{
const labelToUpdate = globalState.filter(entry => entry.value === categoryKey).pop();
const index = globalState.indexOf(labelToUpdate);
labelToUpdate.text = value;
globalState[index] = labelToUpdate
console.log(globalState)
setGlobalState(globalState)
}
I sat my key in euqal to the data.text attribute, so it would update automatically, but that does not happen
The issue here of course, is that i map my categories, but access my globalState object, so therefore it does not automatically update.
You are mutating React state (and React doesn't like this at all). That can trigger weird problems and make things doesn't re-render as expected.
const labelToUpdate = globalState.filter(entry => entry.value === categoryKey).pop();
Pop is a mutable method although I don't know if it's a problem in this case, as filter is purely functional. Anyway you can use find instead of filter (const labelToUpdate = globalState.find(entry => entry.value === categoryKey)) if you only want one element or slice(-1)[0] after filter if there's several elements and you only want the last one (const labelToUpdate = globalState.filter(entry => entry.value === categoryKey).slice(-1)[0])
The function updateLabelValue mutates globalState. In fact you have already changed the state in globalState[index] = labelToUpdate when you invoke setState.
To fix this you can pass the index of the element to the function and make something like this
const updateLabelValue = (value, index) =>{
const newState = globalState.map((item, i) => {
if(index === i){ return value }
else{ return item }
}
setGlobalState(newState)
}

How to add content inside Array using forEach to innerHTML?

I am wondering how can I add data content inside array to innerHTML using the forEach?
For some reason, It is not showing up. Is this possible to do? I am working with dynamic data this way I need to loop through my data and show them with innerHTML.
const showPosts = (data, value) => {
const btnDiscover = document.querySelector('[data-show="discover"]');
const slides = data.add_post_slides;
const Posts = () => {
slides.length > 0 &&
slides
.forEach((el) => {
return <h2>123</h2>;
})
.join('');
};
// console.log(Posts());
btnDiscover.addEventListener('click', (e) => {
e.preventDefault();
body.classList.add('toggle-legend', 'no-scroll');
console.log('Discover clicked');
console.log(slides);
theContent = `
<div class="modal">
<div class="modal__body">
<span class="modal__close"></span>
${Posts()}
</div>
</div>
`;
timelineModal.innerHTML = theContent;
onModalClose();
});
};
How can I do such a thing?
There are many way. I can suggest pick the element by using getElementById. After that create a function that will add the data in innerHtml of that element. Try to do something similar below
var fruits = ["apple", "orange", "cherry"];
fruits.forEach(myFunction);
function myFunction(item, index) {
document.getElementById("demo").innerHTML += index + ":" + item + "<br>";
}
forEach don't return responses. use Array.map or declare and initialize array variables and then push them.
const newSlides = slides.map((el) => el)
or
const elements = [];
slides.forEach((el) => elements.push(el));
// Add return. your arrow function don't return element.
const Posts = () => {
return slides.length > 0 &&
slides
.forEach((el) => {
return <h2>123</h2>;
})
.join('');
};

Categories

Resources