React modal hidden.bs.modal event dosen't work? - reactjs

Modal.js
import React from 'react';
import $ from 'jquery';
const Modal = (props) => {
function onClickHandler(e) {
fetch(
'http://reactapi.test/api/product/delete/' +
e.target.attributes.guid.value,
{
method: 'get',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
}
)
.then((response) => response.json())
.then((data) => {
console.log(data);
});
}
function onModalDismissEvent(e) {
props.onClose(false);
}
$('#exampleModal').on('hidden.bs.modal', (e) => {
console.log(e);
});
return (
<div
className='modal fade'
id='exampleModal'
tabIndex='-1'
aria-labelledby='exampleModalLabel'
aria-hidden='true'
>
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
<h5 className='modal-title' id='exampleModalLabel'>
Resimler
</h5>
<button
type='button'
className='close'
data-dismiss='modal'
aria-label='Close'
onClick={onModalDismissEvent}
>
<span aria-hidden='true'>×</span>
</button>
</div>
<div className='modal-body'>
{props.product.slider.map((e) => {
return (
<div key={e.products_slider_guid}>
<img
height='150px'
src={'http://example.test/' + e.image}
alt={e.created_at}
/>
<button className='btn btn-outline-primary mr-2'>
Update
</button>
<button
onClick={onClickHandler}
className='btn btn-outline-danger mr-2'
guid={e.products_slider_guid}
>
Delete
</button>
</div>
);
})}
</div>
<div className='modal-footer'>
<button className='btn btn-outline-primary mr-4' type='button'>
Save
</button>
</div>
</div>
</div>
</div>
);
};
export default Modal;
Main.js
import React, { useContext, useState } from 'react';
import ProductContext from '../store/product-context';
import Modal from './Modal';
const Main = (props) => {
const [productImageHandler, setProductImageHandler] = useState(false);
const [findProductHandler, setFindProductHandler] = useState('');
const productCtx = useContext(ProductContext);
const onClickHandler = (event) => {
const findProduct = productCtx.items.find((item) => {
return item.product_guid === event.target.attributes.guid.value;
});
setFindProductHandler(findProduct);
setProductImageHandler(true);
};
const onCloseHandler = (e) => {
setProductImageHandler(e);
};
return (
<div>
<main ref={(modal) => modal} role='main' className='container mt-5'>
<div className='row'>
<div className='col-xl-12'>
{productCtx.items.map((item) => {
return (
<div key={item.id} className='list-group'>
<div className='list-group-item d-flex justify-content-between align-items-center'>
{item.name}
<div>
<button
className='btn btn-outline-info mr-2'
data-toggle='modal'
data-target='#exampleModal'
onClick={onClickHandler}
guid={item.product_guid}
>
Images
</button>
<button className='btn btn-outline-danger mr-2'>
Delete
</button>
</div>
</div>
</div>
);
})}
</div>
</div>
{productImageHandler && (
<Modal onClose={onCloseHandler} product={findProductHandler} />
)}
</main>
</div>
);
};
export default Main;
Modal hidden listener never trigger.
I'm trying to trigger the closing event when clicking the modal backdrop, but it doesn't work.
Is there anyone who can help, I'm not sure if I did the logic right now, I just started writing react
I've been dealing with it since yesterday morning React is a bit complicated core javascript is easier :)

after getting a better understanding of your issue at hand, you can add another prop to help to determine whether to show the component or keep it hidden. on each change on the variable in the useEffect array the function will trigger, check out react's docs on useEffect
Here's a simple example:
import React, { useEffect } from 'react';
const Modal = (props) => {
function onModalDismissEvent(e) {
props.onClose(false);
}
useEffect(() => {
return () => {
console.log("changed");
};
}, [props.hidden]) // "hidden" could be replaced with "closed" if such a prop exists that matches "props.onClose"
return (
<div
className='modal fade'
id='exampleModal'
tabIndex='-1'
aria-labelledby='exampleModalLabel'
aria-hidden='true'
>
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
<h5 className='modal-title' id='exampleModalLabel'>
Resimler
</h5>
<button
type='button'
className='close'
data-dismiss='modal'
aria-label='Close'
onClick={onModalDismissEvent}
>
<span aria-hidden='true'>×</span>
</button>
</div>
<div className='modal-body'>
<Product product={props.product} />
</div>
<div className='modal-footer'>
<button className='btn btn-outline-primary mr-4' type='button'>
Save
</button>
</div>
</div>
</div>
</div>
);
};
export default Modal;
//Product.jsx
const Product = ({ product }) => {
function onClickHandler(e) {
fetch(
'http://reactapi.test/api/product/delete/' +
e.target.attributes.guid.value,
{
method: 'get',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
}
)
.then((response) => response.json())
.then((data) => {
console.log(data);
});
}
return (
{
product.slider.map((e) => {
return (
<div key={e.products_slider_guid}>
<img
height='150px'
src={'http://example.test/' + e.image}
alt={e.created_at}
/>
<button className='btn btn-outline-primary mr-2'>
Update
</button>
<button
onClick={onClickHandler}
className='btn btn-outline-danger mr-2'
guid={e.products_slider_guid}
>
Delete
</button>
</div>
);
})
}
)
}

Thank you for your help, I understand what you mean.

Related

Errors while trying to convert Class Component to Functional

I'm trying to convert the class components in my Reactjs ToDo app to functional components. Here is one of the my original components:
import React, { Component } from "react";
import TaskDataService from "../services/task.service";
import FadeIn from 'react-fade-in';
import { Link } from "react-router-dom";
export default class TasksList extends Component {
constructor(props) {
super(props);
this.onChangeSearchTitle = this.onChangeSearchTitle.bind(this);
this.retrieveTasks = this.retrieveTasks.bind(this);
this.refreshList = this.refreshList.bind(this);
this.setActiveTask = this.setActiveTask.bind(this);
this.removeAllTasks = this.removeAllTasks.bind(this);
this.searchTitle = this.searchTitle.bind(this);
this.state = {
Tasks: [],
currentTask: null,
currentIndex: -1,
searchTitle: ""
};
}
componentDidMount() {
this.retrieveTasks();
}
onChangeSearchTitle(e) {
const searchTitle = e.target.value;
this.setState({
searchTitle: searchTitle
});
}
retrieveTasks() {
TaskDataService.getAll()
.then(response => {
this.setState({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
refreshList() {
this.retrieveTasks();
this.setState({
currentTask: null,
currentIndex: -1
});
}
setActiveTask(Task, index) {
this.setState({
currentTask: Task,
currentIndex: index
});
}
removeAllTasks() {
TaskDataService.deleteAll()
.then(response => {
console.log(response.data);
this.refreshList();
})
.catch(e => {
console.log(e);
});
}
searchTitle() {
this.setState({
currentTask: null,
currentIndex: -1
});
TaskDataService.findByTitle(this.state.searchTitle)
.then(response => {
this.setState({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
render() {
const { searchTitle, Tasks, currentTask, currentIndex } = this.state;
return (
<FadeIn>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={this.onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={this.searchTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Tasks List</h4>
<FadeIn>
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li
className={
"list-group-item " +
(index === currentIndex ? "active" : "")
}
onClick={() => this.setActiveTask(Task, index)}
key={index}
>
<div className="align-left">
{Task.title}
</div>
<div className="align-right">
{Task.startDate.toString().split("T")[0]}
</div>
</li>
))}
</ul>
</FadeIn>
<button
className="m-3 btn btn-sm btn-danger"
onClick={this.removeAllTasks}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentTask ? (
<FadeIn>
<div>
<h4>Task</h4>
<div>
<label>
<strong>Title:</strong>
</label>{" "}
{currentTask.title}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{currentTask.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div>
<label>
<strong>Due Date:</strong>
</label>{" "}
{currentTask.startDate.split("T")[0]}
</div>
<Link
to={"/Tasks/" + currentTask.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
</FadeIn>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</div>
</FadeIn>
);
}
}
...and my attempt to convert it into a functional component:
import React, { useState, useEffect } from "react";
import TaskDataService from "../services/task.service";
import FadeIn from 'react-fade-in';
import { Link } from "react-router-dom";
function TasksList() {
const [Tasks, setTasks] = useState([]);
const [currentTask, setCurrentTask] = useState(null);
const [currentIndex, setCurrentIndex] = useState(-1);
const [searchTitle, setSearchTitle] = useState("");
useEffect(() => {
retrieveTasks();
});
const onChangeSearchTitle = (e) => {
const searchTitle = e.target.value;
setSearchTitle({
searchTitle: searchTitle
});
}
const retrieveTasks = () => {
TaskDataService.getAll()
.then(response => {
setTasks({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
const refreshList = () => {
retrieveTasks();
setTasks(null);
setCurrentIndex(-1);
}
const setActiveTask = (Task, index) => {
setTasks( Task );
setCurrentIndex( index );
}
const removeAllTasks = () => {
TaskDataService.deleteAll()
.then(response => {
console.log(response.data);
refreshList();
})
.catch(e => {
console.log(e);
});
}
const onSearchTitle = () => {
setTasks(null);
setCurrentIndex(-1);
TaskDataService.findByTitle(searchTitle)
.then(response => {
setTasks(response.data)
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
return (
<FadeIn>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={onSearchTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Tasks List</h4>
<FadeIn>
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li
className={
"list-group-item " +
(index === currentIndex ? "active" : "")
}
onClick={() => setActiveTask(Task, index)}
key={index}
>
<div className="align-left">
{Task.title}
</div>
<div className="align-right">
{Task.startDate.toString().split("T")[0]}
</div>
</li>
))}
</ul>
</FadeIn>
<button
className="m-3 btn btn-sm btn-danger"
onClick={removeAllTasks}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentTask ? (
<FadeIn>
<div>
<h4>Task</h4>
<div>
<label>
<strong>Title:</strong>
</label>{" "}
{currentTask.title}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{currentTask.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div>
<label>
<strong>Due Date:</strong>
</label>{" "}
{currentTask.startDate.split("T")[0]}
</div>
<Link
to={"/tasks/" + currentTask.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
</FadeIn>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</div>
</FadeIn>
);
}
export default TasksList;
But when running my frontend, the page fails to load with this error seeming like the big one: "Uncaught TypeError: Tasks.map is not a funtion". Console points to this segment as the culprit:
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li...
So it doesn't like Tasks.map. I've spent a lot of time on research and can't figure out what the problem is. Any ideas?
It seems that you are settings in multiple part of your code with setTasks a value other than an array which eventually can leads to Tasks.map triggering an error.
For exemple in the function retreiveTasks you are setting Tasks to an object.

Next js - How to Update or Clear/Reset const array value?

I am Beginner to fronted development.I am working on next js project.
My task is to display result on form submit, Which i have done. Next task is to reset form and also clear/reset search result on button click. I am not able to find the solution, how do i can reset/clear that.
Here is What i have tried so far:
UPDATED
import { useForm } from 'react-hook-form';
import { useState } from "react";
import { yupResolver } from '#hookform/resolvers/yup';
import * as Yup from 'yup';
const userEndPoint = '/api/users/';
const { searchResults = [] } = [];
export default function Home() {
const [userSearchResults, setUserSearchResults] = useState({ result: [] });
const validationSchema = Yup.object().shape({
searchQuery: Yup.string()
.required('This field is required'),
});
const formOptions = { resolver: yupResolver(validationSchema) };
const { errors } = formState;
const resetFunction = async () => {
setUserSearchResults([]);
}
const onSubmitFunction = async (event) => {
console.log("search query =====> ",event.searchQuery)
const response = await fetch(userEndPoint+event.searchQuery)
const data = await response.json()
searchResults = data.results;
userSearchResults = data.results;
};
return (
<div className="card m-3">
<h5 className="card-header">Example App</h5>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmitFunction)}>
<div className="form-row row">
<div className="col-6 d-flex align-items-center">
<input name="searchQuery" type="text" {...register('searchQuery')} className={`form-control ${errors.searchQuery ? 'is-invalid' : ''}`} />
<div className="invalid-feedback">{errors.searchQuery?.message}</div>
</div>
<div className="col-6">
<button type="submit" className="btn btn-primary mr-1">Search</button>
<button type="button" onClick={() => resetFunction()} className="btn btn-secondary">Clear</button>
</div>
</div>
</form>
</div>
<div className="card-body">
<p className="card-header">Search results: </p>
{userSearchResults}
{ <ul>
{ searchResults.map( result => {
const {id, name, image} = result;
return (
<li key={id} className='card'>
<h3>{name}</h3>
</li>
)
} ) }
</ul> }
</div>
</div>
);
}
Please correct me. Highly appreciated if suggest me with best practices.
Thank you!
I will assume the data returned from your API is an array of objects, please try this and let me know if that works:
export default function Home() {
const [userSearchResults, setUserSearchResults] = useState([]);
const validationSchema = Yup.object().shape({
searchQuery: Yup.string().required("This field is required"),
});
const formOptions = { resolver: yupResolver(validationSchema) };
const { errors } = formState;
const resetFunction = () => {
setUserSearchResults([]);
};
const onSubmitFunction = async (event) => {
console.log("search query =====> ", event.searchQuery);
const userEndPoint = "/api/users/";
const response = await fetch(userEndPoint + event.searchQuery);
const data = await response.json();
setUserSearchResults(data.results);
};
return (
<div className="m-3 card">
<h5 className="card-header">Example App</h5>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmitFunction)}>
<div className="form-row row">
<div className="col-6 d-flex align-items-center">
<input
name="searchQuery"
type="text"
{...register("searchQuery")}
className={`form-control ${
errors.searchQuery ? "is-invalid" : ""
}`}
/>
<div className="invalid-feedback">
{errors.searchQuery?.message}
</div>
</div>
<div className="col-6">
<button type="submit" className="mr-1 btn btn-primary">
Search
</button>
<button
type="button"
onClick={() => resetFunction()}
className="btn btn-secondary"
>
Clear
</button>
</div>
</div>
</form>
</div>
<div className="card-body">
<p className="card-header">Search results: </p>
<ul>
{userSearchResults.length > 0 && userSearchResults.map((result) => {
const { id, name, image } = result;
return (
<li key={id} className="card">
<h3>{name}</h3>
</li>
);
})}
</ul>
</div>
</div>
);
}

Submit and close modal window. React React-Hook-Form Typescript

I want to submit and close with the button "save". change type to "button" type="submit" value="submit" onClick={ onClose } don't submit and don't close. I am using onClose for close the modal
import { useAppDispatch } from '../redux/hooks';
import { userInfo } from '../redux/model';
import { useForm } from 'react-hook-form';
import { ModalProps } from '../redux/model';
import { setList } from '../redux/slice';
type UI = userInfo;
const AddData: React.FC<ModalProps> = ({ isOpen, onClose }) => {
const [ page, setPage ] = useState(0);
const FormTitles = ["Invoice Address", "Bank Data", "Contact"];
const dispatch = useAppDispatch();
const { register, handleSubmit, formState: { errors, isValid }} = useForm<UI>({ mode: "all" });
const onSubmit = handleSubmit((data) => {
dispatch(setList(data));
});
return isOpen ? (<div className="viewModal">
<div className='modalContent'>
<form onSubmit={ onSubmit }>
<div className="contentForm">
<div className="closeX" onClick={ onClose }>x</div>
<div className="labels">
<div className="titleTxt">{ FormTitles[page] }</div>
{ page === 0 && (<>
<div className="labelInput">
<label htmlFor="additional">Additional</label>
<input { ...register("additional")} id="additional" />
</div>
<div className="labelInput">
... // content
</div>
<div className="labelInput">
... // content
</div>
</>)}
{ page === 1 && (<>
<div className="labelInput">
<label htmlFor="iban">IBAN</label>
<div className="dirCol">
<input { ...register("iban", { required: true, maxLength: 30 })} id="iban" />
<div className="required">{ errors.iban && "This is required!" }</div>
</div>
</div>
<div className="labelInput">
... // content
</div>
{ page === 2 && (<>
{/* *** fax *** */}
<div className="labelInput">
<label htmlFor="fax">Fax</label>
<input { ...register("fax")} id="fax" />
</div>
<div className="labelInput">
... // content
</div>
<div className="labelInput">
... // content
</div>
</div>
<div className="labelButton">
<button className="button2" onClick={ onClose }>Cancel</button>
{ page !== 0 && (<div className="button2 textCancel" onClick={() => { setPage((x) => x - 1)}}
>Previous</div>)}
{ page !== 2 && (<button className="button1" type="button" disabled={ !isValid } onClick={() => { setPage((x) => x + 1)}}
>Next</button>)}
{ page === 2 && (<>
<button className="button1" type="submit" value="submit" onClick={ onClose }>Save</button>
</>)}
</div>
</div>
</form>
</div>
</div>) : null
};
export default AddData;
here AddData off and on, onClose working with cancel and x button
import { useState } from 'react';
import AddData from './AddData';
import { useAppDispatch, useAppSelector } from '../redux/hooks';
import { removeList } from '../redux/slice';
const ListTable = () => {
const [ isModalOpen, setIsModalOpen ] = useState(false);
const toogleModal = () =>setIsModalOpen(!isModalOpen);
const dispatch = useAppDispatch();
const selector = useAppSelector((state) => state.users.list );
return <>
<AddData isOpen={ isModalOpen } onClose={ toogleModal }>
</AddData>
<nav>
<button onClick={ toogleModal }>Add</button>
</nav>
<div className='content'>
... // content
</div>
</>
};
export default ListTable;
Inside of the onClick for your save button you can call onSubmit and onClose.
import { useAppDispatch } from "../redux/hooks";
import { userInfo } from "../redux/model";
import { useForm } from "react-hook-form";
import { ModalProps } from "../redux/model";
import { setList } from "../redux/slice";
type UI = userInfo;
const AddData: React.FC<ModalProps> = ({ isOpen, onClose }) => {
const [page, setPage] = useState(0);
const FormTitles = ["Invoice Address", "Bank Data", "Contact"];
const dispatch = useAppDispatch();
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<UI>({ mode: "all" });
const onSubmit = handleSubmit((data) => {
dispatch(setList(data));
});
return isOpen ? (
<div className="viewModal">
<div className="modalContent">
<form onSubmit={onSubmit}>
<div className="contentForm">
<div className="closeX" onClick={onClose}>
x
</div>
<div className="labels">
<div className="titleTxt">{FormTitles[page]}</div>
{page === 0 && (
<>
<div className="labelInput">
<label htmlFor="additional">Additional</label>
<input {...register("additional")} id="additional" />
</div>
<div className="labelInput">{/* ... // content */}</div>
<div className="labelInput">{/* ... // content */}</div>
</>
)}
{page === 1 && (
<>
<div className="labelInput">
<label htmlFor="iban">IBAN</label>
<div className="dirCol">
<input
{...register("iban", { required: true, maxLength: 30 })}
id="iban"
/>
<div className="required">
{errors.iban && "This is required!"}
</div>
</div>
</div>
<div className="labelInput">... // content</div>
</>
)}
{page === 2 && (
<>
{/* *** fax *** */}
<div className="labelInput">
<label htmlFor="fax">Fax</label>
<input {...register("fax")} id="fax" />
</div>
<div className="labelInput">{/* ... // content */}</div>
<div className="labelInput">{/* ... // content */}</div>
</>
)}
</div>
<div className="labelButton">
<button className="button2" onClick={onClose}>
Cancel
</button>
{page !== 0 && (
<div
className="button2 textCancel"
onClick={() => {
setPage((x) => x - 1);
}}
>
Previous
</div>
)}
{page !== 2 && (
<button
className="button1"
type="button"
disabled={!isValid}
onClick={() => {
setPage((x) => x + 1);
}}
>
Next
</button>
)}
{page === 2 && (
<>
<button
className="button1"
type="submit"
value="submit"
onClick={() => {
onSubmit();
onClose;
}}
>
Save
</button>
</>
)}
</div>
</div>
</form>
</div>
</div>
) : null;
};
export default AddData;

Cannot receive to get clicked button value in React.js

I tried to get the button value when I click the button (onClick method).
I tried to do generally the way that is e.target.value but sometimes it returns an undefined or empty string. Even I tried to arrow function way that I find in here but still doesn't work. Could you help me with this problem? Which part is wrong or do I miss something?
This is the main part of my code:
updateProducts = e => {
// e.preventDefault();
let buttonValue = e.target.value;
console.log(buttonValue);
fetch('/data/MainProductsShoes.json', {})
.then(res => res.json())
.then(data => {
this.setState({
productsInfo: data,
// category_id: '',
});
});
};
and
<main className="contentsBody">
<div className="contentsLink">
<div className="viewAll">
<button onClick={this.updateProducts} value="viewAll">
<span>전체보기</span>
</button>
</div>
<div className="Shoes">
<button onClick={this.updateProducts} value="shoes">
<span>신발</span>
</button>
</div>
<div className="Clothes">
<button onClick={this.updateProducts} title="clothing">
<span>의류</span>
</button>
</div>
<div className="Supplies">
<button onClick={this.updateProducts} title="acc">
<span>용품</span>
</button>
</div>
</div>
Here's the complete code:
import React, { Component } from 'react';
import './Main.scss';
import COLOR_LISTS from './colorList';
import Products from './ProductsInfo/Products';
export class Main extends Component {
constructor() {
super();
this.state = {
productsInfo: [],
};
}
// const API = "DASDDADDAD" => 컴포넌트 마다 다 적용할수x
componentDidMount() {
fetch('/data/MainProducts.json', {})
.then(res => res.json())
.then(data => {
this.setState({
productsInfo: data,
});
});
}
updateProducts = e => {
// e.preventDefault();
let buttonValue = e.target.value;
console.log(buttonValue);
fetch('/data/MainProductsShoes.json', {})
.then(res => res.json())
.then(data => {
this.setState({
productsInfo: data,
// category_id: '',
});
});
};
// updateProducts = () => {
// console.log('clicked');
// fetch('/data/MainProducts.json', {
// method: 'GET',
// })
// .then(res => res.json())
// .then(data => {
// this.setState({
// productsInfo: data,
// category_id: this.state.category_id,
// });
// });
// };
// showToProductDetailPage = () => {
// const { productsInfo } = this.state;
// productsInfo &&
// productsInfo.map((product, idx) => (
// <Products key={idx} productInfo={product} />
// ));
// };
render() {
const { productsInfo } = this.state;
return (
<div className="MainWrapper">
<div className="sectionHeader">
<div className="sectionTitle">
<div className="sectionTitleCategory">
<span className="categoryName">Men</span>
<br />
<br />
<span>Men's 의류(12)</span>
</div>
<div className="sectionControl">
<button>필터</button>
<select name="list" className="productsFilter">
<option value="newestOrdeer">신상품순</option>
<option value="hightCostoOder">높은가격순</option>
<option value="lowCostOrder">낮은가격순</option>
</select>
</div>
</div>
</div>
<div className="contentsWrapper">
<aside className="contentsSide">
<div className="HorizontalLine" />
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" title="베이지" key={idx}>
<input type="checkbox" />
<label
className="checkboxLabel"
for="checkbox"
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
<div className="sizes">
<div className="HorizontalLine" />
<span>사이즈</span>
<div className="sizeLists">
<button title="230">230</button>
<button title="230">235</button>
<button title="230">240</button>
<button title="230">245</button>
<button title="230">250</button>
<button title="230">255</button>
<button title="230">260</button>
<button title="230">265</button>
<button title="230">270</button>
<button title="230">275</button>
<button title="230">280</button>
</div>
<div className="HorizontalLine" />
</div>
</aside>
<main className="contentsBody">
<div className="contentsLink">
<div className="viewAll">
<button onClick={this.updateProducts} value="viewAll">
<span>전체보기</span>
</button>
</div>
<div className="Shoes">
<button onClick={this.updateProducts} value="shoes">
<span>신발</span>
</button>
</div>
<div className="Clothes">
<button onClick={this.updateProducts} title="clothing">
<span>의류</span>
</button>
</div>
<div className="Supplies">
<button onClick={this.updateProducts} title="acc">
<span>용품</span>
</button>
</div>
</div>
<article className="productsMapping">
{productsInfo &&
productsInfo.map((product, idx) => (
<Products key={idx} productInfo={product} />
))}
</article>
</main>
</div>
</div>
);
}
}
export default Main;
change you updateProducts function to receive a string value representing the product name:
updateProducts = (name) => {
console.log(name);
fetch('/data/MainProductsShoes.json', {})
.then(res => res.json())
.then(data => {
this.setState({
productsInfo: data,
// category_id: '',
});
});
};
Then on each button pass the product name as an argument:
<button onClick={() => this.updateProducts('shoes')}>
Use event.currentTarget.value instead of event.target.value.
let buttonValue = e.currentTarget.value;
console.log(buttonValue);
event.target points to the element where the click event triggered. In your case that can be either span inside the button sometimes and that is when you get undefined value.
event.currentTarget points to the element where the event handler is attached which is the button.
I also notice some of the buttons have title property instead of value property.
<div className="viewAll">
<button onClick={this.updateProducts} value="viewAll">
<span>전체보기</span>
</button>
</div>
<div className="Shoes">
<button onClick={this.updateProducts} value="shoes">
<span>신발</span>
</button>
</div>
<div className="Clothes">
<button onClick={this.updateProducts} title="clothing">
<span>의류</span>
</button>
</div>
<div className="Supplies">
<button onClick={this.updateProducts} title="acc">
<span>용품</span>
</button>
</div>

Irregular status 500 on Apollo GraphQL requests ("Variable X of required type \"String!\" was not provided.")

I get a network error just some of the times I call a lazyQuery in my React project with Apollo and GraphQL. In my app I can successfully get search results back containing various drinks. These are mapped to their own buttons. On clicking one of these buttons a function triggers that queries data about that specific drink.
Most of the time it works fine, but some of the times I get a status 500 error, saying that the cocktailName variable of the type string was not provided. When I've tried requesting the same drinks several times I've sometimes gotten a good response and sometimes not. When I try to debug the function that sends the query, it seems to receive the variable every single time, and I haven't managed to crash the app when I have debug breakpoints in that function. I'd really appreciate any assistance with this!
Main component for the drinks:
import React from 'react';
import { useStoreState, useStoreActions } from 'easy-peasy';
import { useQuery, useLazyQuery } from '#apollo/react-hooks';
import { RANDOM_COCKTAIL_QUERY, ONE_COCKTAIL_BY_NAME_QUERY } from './Queries';
import LoadingSpinner from './LoadingSpinner';
import CocktailSearchFieldAlt from './CocktailSearchFieldAlt';
import { cocktailIngredientList, cocktailMeasureList } from './ingredientLists';
// Imported ingredient and measure lists represent names of keys in objects
export default function Cocktails() {
const dataType = useStoreState((state) => state.cocktailDataType);
const changeDataType = useStoreActions((actions) => actions.changeCocktailDataType);
const dataSet = useStoreState((state) => state.cocktailDataSet);
const changeDataSet = useStoreActions((actions) => actions.changeCocktailDataSet);
const randomizeClick = () => {
if (dataType !== 'randomCocktail') {
changeDataType('randomCocktail');
changeDataSet('data');
}
refetch();
};
const specifikLookup = (drinkName) => {
if (dataType === 'randomCocktail') {
changeDataSet('specificData');
changeDataType('oneCocktailByName');
}
getDrink({ variables: { cocktailName: drinkName } });
};
const { loading, error, data, refetch } = useQuery(RANDOM_COCKTAIL_QUERY);
const [
getDrink,
{ data: specificData, loading: loadingSpecific, error: errorSpecific }
] = useLazyQuery(ONE_COCKTAIL_BY_NAME_QUERY);
if (loading || loadingSpecific) return <LoadingSpinner />;
if (error) return `Error! ${error}`;
if (errorSpecific) return `Error! ${errorSpecific}`;
return (
<section id="cocktail">
<h2 className="mainHeader mt-5 mb-4 scrollTrick">Cocktail</h2>
<div className="card card-body mb-3">
<div className="row">
<div className="col-md-9">
<h4>{eval(dataSet)[dataType].strDrink}</h4>
<p>
{eval(dataSet)[dataType].strAlcoholic}
<br />
Best served in: {eval(dataSet)[dataType].strGlass}
</p>
<h6>Recipee</h6>
<div className="row">
<div className="col-md-4">
<ul>
{eval(dataSet) &&
cocktailIngredientList.map(
(x, i) =>
eval(dataSet)[dataType][x] && (
<li key={i}>{eval(dataSet)[dataType][x]}</li>
)
)}
</ul>
</div>
<div className="col-md-3">
<ul>
{data &&
cocktailMeasureList.map(
(x) => eval(dataSet)[dataType][x] && <li>{eval(dataSet)[dataType][x]}</li>
)}
</ul>
</div>
</div>
<h6>Instructions</h6>
<p>{eval(dataSet)[dataType].strInstructions}</p>
</div>
<div className="col-md-3">
<img src={eval(dataSet)[dataType].strDrinkThumb} alt="Cocktail" className="img-fluid mb-3" />
<button className="btn btn-primary btn-block" onClick={() => randomizeClick()}>
Randomize Cocktail
</button>
</div>
</div>
{/* <CocktailSearchField specificLookup={specifikLookup} /> */}
<CocktailSearchFieldAlt specificLookup={specifikLookup} />
</div>
</section>
);
}
Search component for the drinks:
import React, { useState } from 'react';
import { useStoreState, useStoreActions } from 'easy-peasy';
import { useLazyQuery } from '#apollo/react-hooks';
import { MULTIPLE_COCKTAILS_BY_NAME_QUERY, MULTIPLE_COCKTAILS_BY_INGREDIENT_QUERY } from './Queries';
import LoadingSpinner from './LoadingSpinner';
export default function SearchField(props) {
const [
searchInput,
setSearchInput
] = useState('');
const drinkOptionValue = useStoreState((state) => state.drinkOptionValue);
const changeDrinkOptionValue = useStoreActions((actions) => actions.changeDrinkOptionValue);
const handleSubmit = (e) => {
e.preventDefault();
getDrinks({ variables: { cocktailName: searchInput } });
getDrinksByIngredient({ variables: { ingredientName: searchInput } });
};
const [
getDrinks,
{ loading, data }
] = useLazyQuery(MULTIPLE_COCKTAILS_BY_NAME_QUERY);
const [
getDrinksByIngredient,
{ loading: loadingByIngredient, data: dataByIngredient }
] = useLazyQuery(MULTIPLE_COCKTAILS_BY_INGREDIENT_QUERY);
const lookupDrink = (e) => {
console.log(e.target.value);
changeDrinkOptionValue(e.target.value);
props.specificLookup(e.target.value);
};
if (loading || loadingByIngredient) return <LoadingSpinner />;
return (
<div>
<div className="row border-top mt-3 justify-content-center">
<div className="mt-3">
<form className="form-inline my-2 my-lg-0" onSubmit={handleSubmit}>
<input
className="form-control mr-sm-1"
type="text"
placeholder="Search cocktail"
onChange={(e) => setSearchInput(e.target.value)}
value={searchInput}
/>
<button className="btn btn-secondary my-2 my-sm-0 pl-3 pt-2 pb-2 pr-3" type="submit">
<i className="material-icons mt-2">search</i>
</button>
</form>
</div>
</div>
{data &&
(data.multipleCocktailsByName !== null ? (
<div className="container">
<div className="row">
<div className="col">
<h6 className="mt-3 mb-3 text-center">Search results: </h6>
</div>
</div>
<div className="row justify-content-center">
{dataByIngredient.multipleCocktailsByIngredient !== null &&
dataByIngredient.multipleCocktailsByIngredient.map((cocktailByIngredient) => {
if (
data.multipleCocktailsByName.some(
(cocktail) => cocktail.strDrink === cocktailByIngredient.strDrink
)
) {
return;
} else {
data.multipleCocktailsByName.push(cocktailByIngredient);
}
})}
{
(data.multipleCocktailsByName.sort(
(a, b) => (a.strDrink > b.strDrink ? 1 : b.strDrink > a.strDrink ? -1 : 0)
),
data.multipleCocktailsByName.map((cocktail, i) => (
<button
className="btn btn-outline-secondary p-1 menuButton btnAnimated mr-1 mb-1 border border-secondary"
key={i}
value={cocktail.strDrink}
onClick={lookupDrink}
>
<img src={cocktail.strDrinkThumb} className="menuPicture" alt="cocktail" />
{cocktail.strDrink}
</button>
)))
}
</div>
</div>
) : dataByIngredient.multipleCocktailsByIngredient !== null ? (
<div className="container">
<div className="row">
<div className="col">
<h6 className="mt-3 mb-3 text-center">Search results: </h6>
</div>
</div>
<div className="row justify-content-center">
{dataByIngredient.multipleCocktailsByIngredient.map((cocktail, i) => (
<button
className="btn btn-outline-secondary p-1 menuButton btnAnimated mr-1 mb-1 border border-secondary"
key={i}
value={cocktail.strDrink}
onClick={lookupDrink}
>
<img src={cocktail.strDrinkThumb} className="menuPicture" alt="cocktail" />
{cocktail.strDrink}
</button>
))}
</div>
</div>
) : (
<div className="row justify-content-center">
<p>No matching result</p>
</div>
))}
</div>
);
}

Resources