Delete button - onDelete function - javascript

My delete button worked before but now stopped working when I added a like/dislike satisfaction section for every challenge. Can someone spot the bug for me? This is message I get in the terminal when pushing the button: DELETE /allchallenges/[object%20Object] 200 1.623 ms - 214-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
import React from 'react'
import DefaultLayout from "../layout/Default"
import Challengebox from '../components/Challengebox'
import axios from "axios";
import "./Allchallenges.css"
import { faThumbsUp } from "#fortawesome/free-solid-svg-icons";
import { faThumbsDown } from "#fortawesome/free-solid-svg-icons";
import { faBalanceScale } from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
class Allchallenges extends React.Component {
constructor() {
super()
this.state = {
challenges: []
}
this.onDelete=this.onDelete.bind(this)
this.sortByTitle=this.sortByTitle.bind(this)
this.sortByDescription=this.sortByDescription.bind(this)
this.searchChallenges=this.searchChallenges.bind(this)
this.challengestotal=this.challengestotal.bind(this)
}
componentDidMount(){
axios({
method: "GET",
url: `${process.env.REACT_APP_API_BASE}/allchallenges`,
withCredentials: true
})
.then(response => {
console.log(response)
let challengeslist = response.data;
this.setState({challenges: challengeslist})
})
.catch(error => {
console.log("You've made an error charles: ",error)
})
}
onDelete(challengeId){
axios
.delete(`${process.env.REACT_APP_API_BASE}/allchallenges/${challengeId}`)
.then(response => {
const challenges = this.state.challenges.filter(challenge => challenge._id !== challengeId)
this.setState({challenges})
})
.catch(err => console.log(err))
}
sortByTitle() {
let challengesSortTitle = this.state.challenges.sort((a,b) => {
return a.title > b.title ? 1 : -1
})
this.setState({
challenges:challengesSortTitle
})
}
sortByDescription() {
let challengesSortDescription = this.state.challenges.sort((a,b) => {
return a.description > b.description ? 1 : -1
})
this.setState({
challenges:challengesSortDescription
})
}
searchChallenges(e){ // eslint-disable-next-line
let challengesSearch = this.state.challenges.filter(challenge => {
if(challenge.title){
if(challenge.title.toLowerCase().includes(e.target.value.toLowerCase())){
return true
}
}
})
this.setState({
challenges:challengesSearch
})
}
challengestotal(){
return `${this.state.challenges.length}`
}
// handleLikeDislike(e){
// e.preventDefault()
// }
render(){
return (
<DefaultLayout>
<div className="challengeoverviewlist">
<h1>All challenges</h1>
<div className="headers">
<button onClick={this.sortByTitle} className="sorttitle">
Sort based on TITLE
</button>
<button onClick={this.sortByDescription} className="sortdescription">
Sort based on DESCRIPTION
</button>
<button onClick={this.sortByDescription} className="sortdescription">
Sort based on DAREDEVILS
</button>
<input className="searchbox" type="text" placeholder="Search for a challenge title here..." onChange={this.searchChallenges} />
<p className="challengescounterbox">{this.challengestotal()} challenges</p>
</div>
<div className="challengeboxes">
{
this.state.challenges.map(challenge =>
(
<div className="totalbox" key={challenge._id}>
<div className="likedislikesbox">
<div className="likecontainer">
<div className="leftalignment"><FontAwesomeIcon icon={faThumbsUp} /></div>
{/* onClick={(e)=> this.handleLikeDislike(e)} */}
<p className="likestat">Likes{challenge.likes}</p>
</div>
<div className="dislikecontainer">
<div className="leftalignment"><FontAwesomeIcon icon={faThumbsDown}/></div>
<p className="dislikestat">Dislike</p>
</div>
<div className="satisfactioncontainer">
<div className="leftalignment"><FontAwesomeIcon icon={faBalanceScale}/></div>
<p className="satisfactionstat">Satisf %</p>
</div>
</div>
<Challengebox
key={challenge._id}
id={challenge._id}
title={challenge.title}
description={challenge.description}
/>
<button className="deletebutton" onClick={this.onDelete}>
Delete
</button>
</div>
))
}
</div>
</div>
</DefaultLayout>
)
}
}
export default Allchallenges

It looks like you forgot to pass the challenge id to the handler.
<button className="deletebutton" onClick={() => this.onDelete(challenge._id)}>
Delete
</button>

onClick accepts an event object as its parameter. You have assumed it will be the id of the challenge you want to delete, so you're trying to add it to the query string. This is why you're getting the object error.
Instead, pass onClick an anonymous function and call onDelete with the id:
<button className="deletebutton" onClick={() => this.onDelete(challenge._id)}>
Since you don't need the event, we don't include it in the function, and instead call onDelete with the id from the current mapped element.

Related

Deleting Elements in array by index using Immer in React

I'm building this component using React where I can Add Delete and edit lessons and sections using immer library. However, when I add a new section I cant seem to delete a specific Lesson in the section, it deletes the last lesson created.
And Deleting a specific section is not working as well. Can anyone give me a hint to this problem?
These are the two deletion function that are giving me a hard time:
remove = (sectionIndex, lessonIndex) => {
const nextState = produce(this.state, (draftState) => {
draftState.list[sectionIndex].lessons.splice(lessonIndex, 1);
});
this.setState(nextState);
this.id++;
};
deletesection(sectionIndex, i) {
const nextState = produce(this.state, (draftState) => {
draftState.list[sectionIndex].section.splice(i, 1);
});
this.setState(nextState);
this.id++;
}
Here is the a link to the sandbox reproduction code: https://codesandbox.io/s/serene-forest-hpv7r?file=/src/TestClonereact.jsx
remove actually seemed to be working for me, but I spotted some errors with deletesection:
The function takes two arguments (both of which seem to be the section index), but you only call it with one.
It's not an arrow function, so it will have its own this and won't be able to access this.state.
You are accessing a property .section which does not seem to exist.
Instead of splice you would want to remove the whole section object from the draftState.list array.
deletesection = (sectionIndex) => {
const nextState = produce(this.state, (draftState) => {
delete draftState.list[sectionIndex];
});
this.setState(nextState);
}
My personal preference would be use curried functions rather than passing the sectionIndex all the way down to the Lesson component. Also you can use produce inside a setState callback rather than accessing this.state directly. But those are just suggestions. Here's my tweaked version:
import React from "react";
import "./styles.css";
import EdiText from "react-editext";
import produce from "immer";
import { v4 as uuid } from "uuid";
const Lesson = ({ lesson, onSave, remove }) => {
const { id } = lesson;
return (
<div key={id} id={`sectionlesson-${id}`}>
<div className="section-titles">
<i className="material-icons" id="iconsectionlist" type="button">
list
</i>
<EdiText
type="text"
value="Lesson Title"
onSave={onSave}
key={id}
id={`lesson-${id}`}
/>
<i className="material-icons" id="iconsectiondel" type="button">
text_fields
</i>
<i className="material-icons" id="iconsectiondel" type="button">
smart_display
</i>
<i
className="material-icons"
id="iconsectiondel"
onClick={remove}
type="button"
>
delete
</i>
</div>
<div className="testh"></div>
</div>
);
};
const Section = ({ section, onSave, remove, addlesson, deletesection }) => {
const { id } = section;
return (
<div key={id} id={`sds-${id}`}>
<div className="course-structure-form" key={id} id={`csf1-${id}`}>
<div className="section-heading">
<i className="material-icons" id="iconsection">
api
</i>
<EdiText type="text" value="Section Title" onSave={onSave} />
</div>
{section.lessons.map((lesson, lessonIndex) => (
<Lesson
key={lesson.id}
lesson={lesson}
remove={remove(lessonIndex)}
onSave={onSave}
/>
))}
<div className="addnewlesson" onClick={addlesson}>
<i
className="material-icons"
id="iconsectionde"
role="button"
type="button"
>
add_circle
</i>
<span>Add New Lesson</span>
</div>
<button onClick={deletesection}>Delete Section</button>
</div>
</div>
);
};
class TestClonereact extends React.Component {
constructor(props) {
super(props);
this.state = {
list: []
};
}
onSave = (val) => {
console.log("Edited Value -> ", val);
};
lesson({ id }) {}
addsection = () => {
this.setState(
produce((draftState) => {
draftState.list.push({ id: uuid(), lessons: [] });
})
);
};
addlesson = (sectionIndex) => () => {
this.setState(
produce((draftState) => {
// needs to have a unique id
draftState.list[sectionIndex].lessons.push({ id: uuid() });
})
);
};
remove = (sectionIndex) => (lessonIndex) => () => {
this.setState(
produce((draftState) => {
draftState.list[sectionIndex].lessons.splice(lessonIndex, 1);
})
);
};
deletesection = (sectionIndex) => () => {
this.setState(
produce((draftState) => {
delete draftState.list[sectionIndex];
})
);
};
render() {
return (
<div>
{this.state.list.map((section, i) => (
<Section
key={section.id}
section={section}
remove={this.remove(i)}
addlesson={this.addlesson(i)}
onSave={this.onSave}
deletesection={this.deletesection(i)}
/>
))}
<div className="add-section-button-structure">
<button className="tablink" onClick={this.addsection}>
Add New Section
</button>
<button className="tablink">Clear</button>
<button className="tablink">Preview</button>
<button className="tablink">Submit</button>
</div>
</div>
);
}
}
export default TestClonereact;
Code Sandbox Link

How to add the product to the favorites?

I am currently making a project over the database I created using Mock API. I created a button, created addToFavorites function. When the button was clicked, I wanted the selected product's information to go to the favorites, but I couldn't. I would be glad if you could help me on how to do this.
(Favorites.js empty now. I got angry and deleted all the codes because I couldn't.)
(
Recipes.js
import React, { useState, useEffect } from "react"
import axios from "axios"
import "./_recipe.scss"
import Card from "../Card"
function Recipes() {
const [recipes, setRecipes] = useState([])
const [favorites, setFavorites] = useState([])
useEffect(() => {
axios
.get("https://5fccb170603c0c0016487102.mockapi.io/api/recipes")
.then((res) => {
setRecipes(res.data)
})
.catch((err) => {
console.log(err)
})
}, [])
const addToFavorites = (recipes) => {
setFavorites([...favorites, recipes])
console.log("its work?")
}
return (
<div className="recipe">
<Card recipes={recipes} addToFavorites={addToFavorites} />
</div>
)
}
export default Recipes
Card.js
import React, { useState } from "react"
import { Link } from "react-router-dom"
import { BsClock, BsBook, BsPerson } from "react-icons/bs"
function Card({ recipes, addToFavorites }) {
const [searchTerm, setSearchTerm] = useState("")
return (
<>
<div className="recipe__search">
<input
type="text"
onChange={(event) => {
setSearchTerm(event.target.value)
}}
/>
</div>
<div className="recipe__list">
{recipes
.filter((recipes) => {
if (searchTerm === "") {
return recipes
} else if (
recipes.title.toLowerCase().includes(searchTerm.toLowerCase())
) {
return recipes
}
})
.map((recipe) => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={350} height={230} />
<h1 className="recipe__card__title">{recipe.title}</h1>
<h3 className="recipe__card__info">
<p className="recipe__card__info--icon">
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h3>
<h3 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h3>
<button type="button" className="recipe__card__cta">
<Link
to={{
pathname: `/recipes/${recipe.id}`,
state: { recipe }
}}
>
View Recipes
</Link>
</button>
<button onClick={() => addToFavorites(recipes)}>
Add to favorites
</button>
</div>
)
})}
</div>
</>
)
}
export default Card
Final Output:
I have implemented the addToFavorite() and removeFavorite() functionality, you can reuse it the way you want.
I have to do bit of modification to the code to demonstrate its working, but underlying functionality of addToFavorite() and removeFavotie() works exactly the way it should:
Here is the Card.js where these both functions are implemented:
import React, { useState } from "react";
import { BsClock, BsBook, BsPerson } from "react-icons/bs";
function Card({ recipes }) {
const [searchTerm, setSearchTerm] = useState("");
const [favorite, setFavorite] = useState([]); // <= this state holds the id's of all favorite reciepies
// following function handles the operation of adding fav recipes's id's
const addToFavorite = id => {
if (!favorite.includes(id)) setFavorite(favorite.concat(id));
console.log(id);
};
// this one does the exact opposite, it removes the favorite recipe id's
const removeFavorite = id => {
let index = favorite.indexOf(id);
console.log(index);
let temp = [...favorite.slice(0, index), ...favorite.slice(index + 1)];
setFavorite(temp);
};
// this variable holds the list of favorite recipes, we will use it to render all the fav ecipes
let findfavorite = recipes.filter(recipe => favorite.includes(recipe.id));
// filtered list of recipes
let filtered = recipes.filter(recipe => {
if (searchTerm === "") {
return recipe;
} else if (recipe.title.toLowerCase().includes(searchTerm.toLowerCase())) {
return recipe;
}
});
return (
<div className="main">
<div className="recipe__search">
<input
type="text"
onChange={event => {
setSearchTerm(event.target.value);
}}
/>
</div>
<div className="recipe-container">
<div className="recipe__list">
<h2>all recipes</h2>
{filtered.map(recipe => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={50} height={50} />
<h2 className="recipe__card__title">{recipe.title}</h2>
<h4 className="recipe__card__info">
<p>
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h4>
<h4 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h4>
<button onClick={() => addToFavorite(recipe.id)}>
add to favorite
</button>
</div>
);
})}
</div>
<div className="favorite__list">
<h2>favorite recipes</h2>
{findfavorite.map(recipe => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={50} height={50} />
<h2 className="recipe__card__title">{recipe.title}</h2>
<h4 className="recipe__card__info">
<p className="recipe__card__info--icon">
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h4>
<h4 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h4>
<button onClick={() => removeFavorite(recipe.id)}>
remove favorite
</button>
</div>
);
})}
</div>
</div>
</div>
);
}
export default Card;
Here is the live working app : stackblitz
You can get the previous favourites recipes and add the new ones.
const addToFavorites = (recipes) => {
setFavorites(prevFavourites => [...prevFavourites, recipes])
console.log("its work?")
}

React js conditionally rendering a class to a specific mapped item

I have been attempting to toggle a class on click so that when I click on one of the mapped items in my Tasks component, I add the 'complete' class and put a line through that item (crossing items off of a todo list). However with my current code set up, when I click on one element to add the class, all the other elements get crossed out as well and vice versa.
Here is my current setup. The class 'complete' is what will add a line through one of the mapped items in the Tasks component.
import { Container, Row} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import axios from 'axios';
const List = (props) =>{
return(
<div>
<Link style={{textDecoration:'none'}} to={`/lists/${props.listId}`} > <p className="list-item">{props.item}</p></Link>
</div>
)
}
const Tasks = (props) =>{
return(
<div onClick={props.onClick} className={props.className} >
<div className='task-item' >
<p >{props.item}</p>
</div>
</div>
)
}
export default class Display extends Component {
constructor(props){
super(props)
this.onCompletedTask = this.onCompletedTask.bind(this);
this.state = {
list: [],
tasks:[],
complete:false
}
}
componentWillUpdate(nextProps){
axios.get(`http://localhost:8080/lists/${this.props.match.params.listId}`)
.then(response =>{
this.setState({
tasks:response.data
})
})
}
componentDidMount(){
axios.get('http://localhost:8080/lists')
.then(response=>{
this.setState({
list:response.data
})
})
.catch(error =>{
console.log(error)
});
}
onCompletedTask(item){
this.setState({ complete: !this.state.complete});
}
listCollection(){
return(
this.state.list.map(item=>{
return(<List item = {item.title} listId={item._id} key = {item._id} />)
})
)
}
taskCollection(){
return(
this.state.tasks.map((item, index) =>{
return(<Tasks onClick = {()=>this.onCompletedTask(item)} className={this.state.complete ? 'complete': ''} item={item.task} key={index}/>)
})
)
}
render() {
return (
<div id='main' >
<Container>
<Row>
<div className="sidebar">
<h1 style={{fontSize:"25pt"}}>Lists</h1>
<div className="list-menu">
{this.listCollection()}
</div>
<form action='/new-list' method='GET'>
<div style={{textAlign:'center'}}>
<button className='list-button' style={{fontSize:'12pt', borderRadius:'5px'}}>
+ New List
</button>
</div>
</form>
</div>
<div className='tasks'>
<h1 style={{fontSize:'25pt'}}>Tasks</h1>
{this.taskCollection()}
<form action={`/lists/${this.props.match.params.listId}/new-task`} method='GET'>
<button className='task-button'>
+
</button>
</form>
</div>
</Row>
</Container>
</div>
)
}
}
Your state holds only a single completed value, which OFC toggle all tasks. You could instead store a map of completed tasks.
this.state = {
list: [],
tasks: [],
complete: {}, // <--- use empty object as simple map object
}
Update onCompletedTask to store some uniquely identifying property of a task, like an id field
onCompletedTask(item){
this.setState(prevState => ({
completed: {
...prevState.completed, // <--- spread existing completed state
[item.id]: !prevState.completed[item.id] // <--- toggle value
},
}));
}
Update. taskCollection to check the completed map by id
taskCollection = () => {
const { completed, tasks } = this.state;
return tasks.map((item, index) => (
<Tasks
onClick={() => this.onCompletedTask(item)}
className={completed[item.id] ? "complete" : ""} // <--- check completed[item.id]
item={item.task}
key={index}
/>
))
};

How to fix ReactJS filtered search that isn't working properly

I am working on some reactjs project that will show data based on user query. User can type and filtered data by checkbox.
It working properly actually, user type something in search bar and record are fetching data correctly. The problem is when user use check box to filter some data, the exact data show up but when they remove/uncheck the checkbox, the filtered data is not show up, instead it show the whole data.
I want that, when user remove/uncheck the checkbox from that data they type, it show back the original data that user type.
I am sorry if my language is a little bit difficult to understand. I will provide my code please have a look and help me to figure out if I made some mistake, also please tell me what is the best practice to fix this problem. I am beginner to React and does not have much knowledge to fix this problem . Thanks
import React from 'react';
import logo from './logo.svg';
import { Dropdown } from 'semantic-ui-react'
import './App.css'
import DropdownCheckbox from './components/dropdownCheckbox/dropdownCheckbox';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
furnitureStyles: [],
products: [],
checkedFS: [],
checkedDT: [],
searchFilt: "",
filteredProduct: []
};
};
async onDropdownChange(val){
await this.setState({
checkedFS: val
})
await this.filteredFurnitureStyle()
}
fetchData(){
fetch('http://www.mocky.io/v2/5c9105cb330000112b649af8')
.then( response => {
if(response.status === 200){
return response.json()
}
})
.then(responseJson => {
this.setState({
furnitureStyles: responseJson.furniture_styles,
products: responseJson.products,
filteredProduct: responseJson.products
})
})
}
componentDidMount(){
this.fetchData()
}
loadingPage(){
return(
<div>
Please Wait ...
</div>
)
}
regexDesc(value){
return value.replace(/(?<=^.{115}).*/, " ...")
}
async filteringFunc(e){
const { checkedDT, checkedFS, searchFilt, products, filteredProduct } = this.state
if(e.target.value !== "") {
let search = products.filter(product => product.name.toLowerCase().indexOf(e.target.value.toLowerCase()) !== -1)
this.setState({
filteredProduct : search
})
} else if(e.target.value === ""){
this.setState({
filteredProduct: products
})
}
}
async onDropdownChange(val){
await this.setState({
checkedFS: val
})
await this.filteredFurnitureStyle()
}
filteredFurnitureStyle = () => {
const { filteredProduct, checkedFS, products } = this.state
if(checkedFS.length > 0) {
let search = filteredProduct.filter(product => (
checkedFS.findIndex(element => product.furniture_style.indexOf(element) !== -1) !== -1
))
this.setState({
filteredProduct: search
})
} else {
this.setState({
filteredProduct: products
})
}
}
render(){
const { furnitureStyles, products, checkedDT, checkedFS, filteredProduct } = this.state
return (
<div className="App">
<header>
<div className="search-section">
<input type="search"
placeholder="Search Furniture ..."
className="search-input"
onChange={(e)=>this.filteringFunc(e)}
/>
</div>
<div className="dropdown-section"
>
{furnitureStyles.length > 0 ? (
<React.Fragment>
<DropdownCheckbox
style={{margin:"0 24px"}}
defaultSelected="Furniture Style"
options={furnitureStyles}
onChange={(val)=>this.onDropdownChange(val)}
/>
<DropdownCheckbox
style={{margin:"0 24px"}}
defaultSelected="Delivery Time"
options={["1 week","2 weeks", "1 Month", "more..."]}
onChange={val=>this.setState({
checkedDT: val
})}
/>
</React.Fragment>) : "Loading"
}
</div>
</header>
<div id="section2">
<div className="card-section">
{products.length > 0 &&
filteredProduct.map(product => {
return (
<div className="ui cards flexing">
<div className="ui fluid card content">
<div className="card-header">
<h4>
{product.name}
</h4>
<span>
IDR {product.price}
</span>
</div>
<div>
<span>
{this.regexDesc(product.description)}
</span>
<div>
<ul className="furniture-styles">
{product.furniture_style.map(style => {
return (
<li>{style}</li>
)
})}
</ul>
</div>
</div>
<div>
<span>{product.delivery_time} {product.delivery_time > 1 ? "days" : "day"}</span>
</div>
</div>
</div>
)
})
}
</div>
</div>
</div>
);
}
}
export default App;
await this.setState isn't gonna work. If you need to call a function after updating a state, use setState's callback function:
onDropdownChange = (val) => {
this.setState({
checkedFS: val
}, this.filteredFurnitureStyle) // Use callback
}

React.js targeting a single element with a shared onClick function

I am new to both coding as well as React.js, so any assistance in learning what I am doing incorrectly is greatly appreciated! I am creating multiple cards on a page with riddles where the answer is hidden via css. I am using an onClick function ("toggleAnswer") to toggle the state of each answer to change the className so that the answer will either be visible or hidden. Currently, the onClick event is changing the state for all the answers. I realize this is because my code is not targeting a particular element, but I am unsure how this can be done. How can this be achieved? My code is currently like this:
// RiddlesPage where toggleAnswer function is defined
class RiddlesPage extends Component {
constructor(props) {
super(props);
this.state = {
questionData: [],
isHidden: true
};
this.getPageData = this.getPageData.bind(this);
this.toggleAnswer = this.toggleAnswer.bind(this);
}
getPageData() {
console.log("we hit getPageData function starting --");
helpers.getRiddlesPage().then(data => {
console.log("this is the result", data);
this.setState({
questionData: data[0].questionData,
});
});
}
toggleAnswer(e) {
this.setState({ isHidden: !this.state.isHidden });
}
componentWillMount() {
this.getPageData();
}
render() {
const answerClass = this.state.isHidden ? "answer-hide" : "answer";
return (
<div>
<Riddles>
{this.state.questionData.map((data, index) => {
return (
<RiddlesItem
key={index}
id={index}
question={data.question}
answer={data.answer}
button={data.buttonURL}
answerClass={answerClass}
onClick={this.toggleAnswer}
/>
);
})}
</Riddles>
</div>
);
}
}
export default RiddlesPage;
// Riddles Component
import React from "react";
import "./riddles.css";
const Riddles = props => (
<div id="riddles-row">
<div className="container">
<div className="row">
<div className="col-12">
<div>{props.children}</div>
</div>
</div>
</div>
</div>
);
export default Riddles;
// RiddlesItem Component where onClick function is set as a prop
import React from "react";
import "./riddles.css";
const RiddlesItem = props => (
<div>
<div className="card-body">
<p id="question">{props.question}</p>
<img
className="img-fluid"
id={props.id}
src={props.button}
onClick={props.onClick}
alt="answer button"
/>
<p className={props.answerClass}> {props.answer} </p>
</div>
</div>
);
export default RiddlesItem;
You'd have to keep track of each answer that has been shown in state (in an array or something).
First
Send the index of the answer up in the onclick function. In that function, check if it exists in the "shownAnswers" array and either add or remove it.
onClick={e => props.onClick(e, props.id)}
and
toggleAnswer(e, index) {
if (this.state.shownAnswers.indexOf(index) > -1) {
this.setState({
shownAnswers: this.state.shownAnswers.filter(val => val !== index)
});
} else {
this.setState({
shownAnswers: this.state.shownAnswers.concat(index)
});
}
}
Then
When you're passing the class name down to the child component, check if its index is in the "shownAnswers" array to decide which class name to pass.
answerClass={this.state.shownAnswers.indexOf(index) > -1 ? "answer" : "answer-hide"}
Building off your example, it could look something like this (untested):
// RiddlesPage where toggleAnswer function is defined
class RiddlesPage extends Component {
constructor(props) {
super(props);
this.state = {
questionData: [],
shownAnswers: []
};
this.getPageData = this.getPageData.bind(this);
this.toggleAnswer = this.toggleAnswer.bind(this);
}
getPageData() {
console.log("we hit getPageData function starting --");
helpers.getRiddlesPage().then(data => {
console.log("this is the result", data);
this.setState({
questionData: data[0].questionData,
});
});
}
toggleAnswer(e, index) {
if (this.state.shownAnswers.indexOf(index) > -1) {
this.setState({ shownAnswers: this.state.shownAnswers.filter(val => val !== index) });
} else {
this.setState({ shownAnswers: this.state.shownAnswers.concat(index) });
}
}
componentWillMount() {
this.getPageData();
}
render() {
return (
<div>
<Riddles>
{this.state.questionData.map((data, index) => {
return (
<RiddlesItem
key={index}
id={index}
question={data.question}
answer={data.answer}
button={data.buttonURL}
answerClass={this.state.shownAnswers.indexOf(index) > -1 ? "answer" : "answer-hide"}
onClick={this.toggleAnswer}
/>
);
})}
</Riddles>
</div>
);
}
}
export default RiddlesPage;
// Riddles Component
import React from "react";
import "./riddles.css";
const Riddles = props => (
<div id="riddles-row">
<div className="container">
<div className="row">
<div className="col-12">
<div>{props.children}</div>
</div>
</div>
</div>
</div>
);
export default Riddles;
// RiddlesItem Component where onClick function is set as a prop
import React from "react";
import "./riddles.css";
const RiddlesItem = props => (
<div>
<div className="card-body">
<p id="question">{props.question}</p>
<img
className="img-fluid"
id={props.id}
src={props.button}
onClick={e => props.onClick(e, props.id)}
alt="answer button"
/>
<p className={props.answerClass}> {props.answer} </p>
</div>
</div>
);
export default RiddlesItem;

Categories