react populate modal from graphql - reactjs

I have a list of products in a react component:
I am trying to display a modal when I click on the specific product that displays the product information through a graphql query. The query works fine. I'm just having problems displaying the modal.
Nothing happens when I click except my console.log statement. DisplayProduct never really gets loaded except when the page is loaded. How do I trigger a reload of DisplayProduct on click.
I'm pretty new to react, hence the issue...
ProductList.jsx
/* eslint-disable no-unused-vars */
import { useState, useCallback } from "react";
import { ResourceList, TextStyle, Stack, Thumbnail } from "#shopify/polaris";
import { Layout } from "#shopify/polaris";
import { DisplayProduct } from "./DisplayProduct";
export function ProductsList({ data }) {
const [open, setOpen] = useState(false);
const [clickedItem, setClickedItem] = useState(false);
const [active, setActive] = useState(false);
return (
<Layout>
<ResourceList // Defines your resource list component
showHeader
resourceName={{ singular: `Product`, plural: `Products` }}
items={data.nodes}
renderItem={(item) => {
const media = (
<Thumbnail
source={
item.images.edges[0] ? item.images.edges[0].node.originalSrc : ``
}
alt={item.images.edges[0] ? item.images.edges[0].node.altText : ``}
/>
);
const price = item.variants.edges[0].node.price;
return (
<ResourceList.Item
id={item.id}
media={media}
accessibilityLabel={`View details for ${item.title}`}
onClick={() => {
setClickedItem(item.id);
setOpen(true);
console.log(`itemId`, clickedItem);
}}
onCancel={() => setOpen(false)}
>
<Stack>
<Stack.Item fill>
<h3>
<TextStyle variation="strong">{item.title}</TextStyle>
</h3>
</Stack.Item>
<Stack.Item>
<p>${price}</p>
</Stack.Item>
</Stack>
</ResourceList.Item>
);
}}
/>
<Layout.Section>
<div style={{ height: `500px` }}>
<Modal
onClose={() => {
setActive(false);
setOpen(false);
console.log(`clicked close button: `, { active });
}}
title="Display Product"
open={active}
loading={false}
limitHeight={false}
sectioned={true}
show={active}
>
<Modal.Section>
<TextContainer>
<DisplayProduct productId={clickedItem} />
</TextContainer>
</Modal.Section>
</Modal>
</div>
</Layout.Section>
</Layout>
);
}
DisplayProduct.jsx
/* eslint-disable no-unused-vars */
import { useState, useCallback } from "react";
import { Banner, Button, Modal, TextContainer } from "#shopify/polaris";
import { gql, useQuery } from "#apollo/client";
import { Loading } from "#shopify/app-bridge-react";
const GET_PRODUCT_BY_ID = gql`
query getProductsById($id: ID!) {
product(id: $id) {
createdAt
defaultCursor
description
descriptionHtml
vendor
}
}
`;
export function DisplayProduct({ productId }) {
const [active, setActive] = useState(true);
const { loading, error, data, refetch } = useQuery(GET_PRODUCT_BY_ID, {
variables: { id: productId },
});
if (loading || typeof data === `undefined`) {
console.log(`loading`);
return <Loading />;
}
if (error) {
console.warn(error);
return (
<Banner status="critical">There was an issue loading products.</Banner>
);
}
console.log(`productId: `, productId);
console.log(`data: `, data);
return (
<p>
display our product here {data.id} and {productId}
</p>
);
}

New to react is right!
I needed a conditional display for the modal. If open is true, display the modal. When the modal is closed, the dom element is removed and then reloaded when open is set to true. I'm not sure if I can refine that down even further so it doesn't have to rebuild the modal each time a product is clicked
Here is the complete .jsx:
import { useState, useCallback } from "react";
import {
ResourceList,
TextStyle,
Stack,
Thumbnail,
Button,
Modal,
TextContainer,
Layout,
} from "#shopify/polaris";
import { DisplayProduct } from "./DisplayProduct";
export function ProductsList({ data }) {
const [open, setOpen] = useState(false);
const [clickedItem, setClickedItem] = useState(false);
const { prodData, setProdData } = useState(false);
return (
<Layout>
<ResourceList // Defines your resource list component
showHeader
resourceName={{ singular: `Product`, plural: `Products` }}
items={data.nodes}
renderItem={(item) => {
const media = (
<Thumbnail
source={
item.images.edges[0] ? item.images.edges[0].node.originalSrc : ``
}
alt={item.images.edges[0] ? item.images.edges[0].node.altText : ``}
/>
);
const price = item.variants.edges[0].node.price;
return (
<ResourceList.Item
id={item.id}
media={media}
accessibilityLabel={`View details for ${item.title}`}
onClick={() => {
setClickedItem(item.id);
setOpen(true);
}}
onCancel={() => setOpen(false)}
>
<Stack>
<Stack.Item fill>
<h3>
<TextStyle variation="strong">{item.title}</TextStyle>
</h3>
</Stack.Item>
<Stack.Item>
<p>${price}</p>
</Stack.Item>
</Stack>
</ResourceList.Item>
);
}}
/>
<Layout.Section> /* here is the important part. Conditionally load the modal and therefore, DisplayProduct. */
{open ? (
<div style={{ height: `500px` }}>
<Modal
onClose={() => {
setOpen(false);
}}
title="Display Product"
open={open}
loading={false}
limitHeight={false}
sectioned={true}
>
<Modal.Section>
<TextContainer>
<DisplayProduct productId={clickedItem} />
</TextContainer>
</Modal.Section>
</Modal>
</div>
) : (
``
)}
</Layout.Section>
</Layout>
);
}

Related

React Outside Hook not reacting to outside Click

I am finishing up a project where I want to use a small dropdown menu when I click on my settings icon. The problem is that for some reason it is not recognized when I click outside of that dropdown menu. I used a hook that worked in the same project with a different dropdown menu, but now it doesn't. Maybe because it is in a modal? I really don't know.
Here is the Repo of this Project: https://github.com/Clytax/fem-kanban
The Hook (I modified it a bit by excluding the elipsis icon so it doesnt reopen when Click on it to close it.)
import React from "react";
export const useOutsideClick = (callback, exclude) => {
const ref = React.useRef();
React.useEffect(() => {
const handleClick = (e) => {
if (
ref.current &&
!ref.current.contains(e.target) &&
!exclude.current.contains(e.target)
) {
callback();
}
};
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
}, [callback, exclude]);
return ref;
};
The Modal:
import React, { useRef, useState, useEffect } from "react";
import "./taskModal.scss";
import { ReactComponent as Elipsis } from "../../../assets/Icons/icon-vertical-ellipsis.svg";
import { ReactComponent as Close } from "../../../assets/Icons/icon-chevron-up.svg";
import { ReactComponent as Open } from "../../../assets/Icons/icon-chevron-down.svg";
import { useSelector, useDispatch } from "react-redux";
import modalSlice, {
closeViewTaskModal,
openEditTaskModal,
closeAllModals,
openDeleteTaskModal,
} from "../../../features/global/modalSlice";
import Backdrop from "../Backdrop/Backdrop";
import Subtask from "../../Task/Subtask";
import "../../Extra/DropdownSettings.scss";
import { useOutsideClick } from "../../../hooks/useOutsideClick";
import { motion } from "framer-motion";
import DropdownStatus from "../../Extra/DropdownStatus";
import DropdownSettings from "../../Extra/DropdownSettings";
import DropdownSettingsTask from "../../Extra/DropdownSettingsTask";
const ViewTaskModal = ({ handleClose }) => {
const [openSettings, setOpenSettings] = useState(false);
const dispatch = useDispatch();
const task = useSelector((state) => state.modal.viewTaskModal.task);
const handleCloseSettings = () => {
console.log("hi");
setOpenSettings(false);
};
const modal = useSelector((state) => state.modal);
const viewTaskModal = useSelector((state) => state.modal.viewTaskModal);
const elipsisRef = useRef(null);
const wrapperRef = useOutsideClick(handleCloseSettings, elipsisRef);
const getFinishedSubTasks = () => {
let finishedSubTasks = 0;
task.subTasks.forEach((subtask) => {
if (subtask.isDone) {
finishedSubTasks++;
}
});
return finishedSubTasks;
};
const closeModal = () => {
dispatch(closeViewTaskModal());
};
return (
<Backdrop onClick={closeModal} mobile={false}>
<motion.div
onClick={(e) => {
e.stopPropagation();
}}
onMouseDown={(e) => {
e.stopPropagation();
}}
className="view-task"
>
<div className="view-task__header | flex">
<h2 className="view-task__header__title">{task.name}</h2>
<div className="view-tastk__settings">
<div
className="view-task__header__icon"
style={{ cursor: "pointer" }}
ref={elipsisRef}
onClick={() => {
setOpenSettings(!openSettings);
}}
>
<Elipsis />
</div>
{openSettings && (
<div className="dropdown-settings__task" ref={wrapperRef}>
<div
className="dropdown-settings__item"
onClick={() => {
dispatch(closeAllModals());
dispatch(openEditTaskModal(task));
}}
>
Edit Task
</div>
<div
className="dropdown-settings__item"
onClick={() => {
dispatch(closeAllModals());
dispatch(openDeleteTaskModal(task));
}}
>
Delete Task
</div>
</div>
)}
</div>
</div>
<p className="view-task__description">{task.description}</p>
<div className="view-task__subtasks">
<p>
Subtasks ({getFinishedSubTasks()} of {task.subTasks.length})
</p>
<div className="view-task__subtasks__list">
{task.subTasks.map((subtask, index) => (
<Subtask
subtaskID={subtask.id}
boardID={task.boardID}
taskID={task.id}
columnID={task.columnID}
key={index}
/>
))}
</div>
</div>
<div className="view-task__status">
<p>Current Status</p>
<DropdownStatus click={handleCloseSettings} task={task} />
</div>
</motion.div>
</Backdrop>
);
};
export default ViewTaskModal;

Trying to get Material ui dialog into contextualised modal

I have a modal component which I have put into context.
If I return html , some divs etc, then no problem, but
ideally I want to do this with the material-ui dialog.
But if I put in materail-ui dialog, as below,
then nothing is displayed.
The component:
import React, { useCallback, useEffect, useState } from 'react'
import {Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "#material-ui/core";
import {useTranslation} from "react-i18next";
import {errorStyles} from "../errorStyle";
// Create a React context
const ModalContext = React.createContext(null);
//Declare the modal component
const Modal = ({ modal, unSetModal, errorClasses = errorStyles(),
title, subtitle, children, }) => {
useEffect(() => {
const bind = e => {
if (e.keyCode !== 27) {
return
}
if (document.activeElement && ['INPUT', 'SELECT'].includes(document.activeElement.tagName)) {
return
}
unSetModal()
}
document.addEventListener('keyup', bind)
return () => document.removeEventListener('keyup', bind)
}, [modal, unSetModal])
const { t } = useTranslation()
return (
<>
<Dialog
className={errorClasses.modal}
fullWidth
maxWidth='md'
aria-labelledby='max-width-dialog-title'
>
<DialogTitle id='max-width-dialog-title'
className={errorClasses.header}>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
</DialogContent>
<DialogActions>
<button className={errorClasses.cancelButton}
onClick={unSetModal}>{t("Close")}</button>
</DialogActions>
</Dialog>
</>
)
}
const ModalProvider = props => {
const [modal, setModal] = useState()
const unSetModal = useCallback(() => {
setModal('')
}, [setModal])
return (
<ModalContext.Provider value={{ unSetModal, setModal }} {...props} >
{props.children}
{modal && <Modal modal={modal} unSetModal={unSetModal} />}
</ModalContext.Provider>
)
}
// useModal: shows and hides the Modal
const useModal = () => {
const context = React.useContext(ModalContext)
if (context === undefined) {
throw new Error('useModal must be used within a UserProvider')
}
return context
}
export { ModalProvider, useModal }
And in another component (an edit form)
import { useModal, ModalProvider } from '../context/modal-context'
.....
export function DeviceModal (props) {
const { setModal } = useModal()
...
some other markup
<div style={{ position: 'sticky', bottom:'0', width:'100%', height:'40px', paddingTop:'5px'}}>
<Grid container direction="row-reverse" item xs={12} alignItems="right">
<button className={classes.cancelButton}
onClick={() => { setModal(<></>)}} >
{'Cancel'}
</button>
</Grid>
</div>
Hope someone can help
Thanks
Answer:
By adding the following to the dialogprops, it is resolved.
open={true}
backdropComponent={Backdrop}
and also, getting it to display values from the parent.
const ModalProvider = props => {
const [modal, setModal] = useState();
const [title, setTitle] = useState();
const [errMsg, setErrMsg] = useState();
const unSetModal = useCallback(() => {
setModal('')
}, [setModal])
// Provide the Modals functions and state variables to the consuming parent component
return (
<ModalContext.Provider value={{ unSetModal, setModal, setTitle, setErrMsg }} {...props} >
{props.children}
{modal && <Modal modal={modal} unSetModal={unSetModal} title={title} errMsg={errMsg} />}
</ModalContext.Provider>
)}
In the parent use setTitle("yada yada") setErrMsg('more yerra yada");

Build search functionality in Next.js app

I want to integrate a search functionality in my Next.js app which should look like this:
User types a keyword in searchBar which is inside of Header
After clicking on the search button the keyword should be passed on the SearchResult page, in which I load it dynamically into the youtube-search-api link
The structure looks like this:
Header.js (contains SearchBar component)
_app.js (contains Header and components)
SearchResults.js (inside pages folder, should fetch typed keyword from Header)
I tried it with zustand and it looks like this currently:
Header.js:
import SearchBar from "./SearchBar";
const Header = () => {
return (
<div className={styles.header}>
...
<SearchBar />
...
</div>
);
};
export default Header;
SearchBar.js:
import create from 'zustand';
import { useRouter } from 'next/router';
import Image from 'next/image';
import styles from '../styles/Header.module.scss';
import loupe from '../public/images/loupe.png';
const useStore = create((set) => ({
keyword: '',
setKeyword: (keyword) =>
set((state) => ({
...state,
keyword,
})),
}));
const SearchBar = () => {
const router = useRouter();
const keyword = useStore((state) => state.keyword);
const setKeyword = useStore((state) => state.setKeyword);
const handleClick = (e) => {
e.preventDefault();
router.push('/searchResults');
};
return (
<div className={styles.searchBar}>
<input
type='text'
value={keyword}
placeholder='Suche...'
onChange={(e) => setKeyword(e.target.value)}
/>
<button className={styles.searchBtn} type='submit' onClick={handleClick}>
<Image src={loupe} alt='' />
</button>
</div>
);
};
export default SearchBar;
_app.js:
import Header from '../components/Header';
function MyApp({ Component, pageProps }) {
return (
<>
<Header />
<Component {...pageProps} />
</>
);
}
export default MyApp;
and SearchResults.js:
import { fetchData } from '../lib/utils';
import Moment from 'react-moment';
import { Modal } from 'react-responsive-modal';
import ReactPlayer from 'react-player/youtube';
export default function SearchResults({ videos }) {
console.log(videos);
const [modalIsOpen, setModalIsOpen] = useState(false);
const [modalData, setModalData] = useState(null);
const videoURL = 'https://www.youtube.com/watch?v=' + modalData;
const sortedVids = videos
.sort((a, b) =>
Number(
new Date(b.snippet.videoPublishedAt) -
Number(new Date(a.snippet.videoPublishedAt))
)
)
.filter((vid) => vid.snippet.title.toLowerCase());
return (
<>
<div className={`${styles.playlist_container} ${styles.search}`}>
<div className={styles.main_container}>
<h1>Search results</h1>
{sortedVids
.filter((v) => v.snippet.title !== 'Private video')
.map((vid, id) => {
return (
<div className={styles.item_container}
key={id}>
<div className={styles.clip_container}>
<Image
className={styles.thumbnails}
src={vid.snippet.thumbnails.medium.url}
layout='fill'
objectFit='cover'
alt={vid.snippet.title}
/>
<button
className={styles.playBtn}
onClick={() => {
setModalData(vid.snippet.resourceId.videoId);
console.log(modalData);
setModalIsOpen(true);
}}
>
<Image src='/images/play.svg' width='60' height='60' />
</button>
</div>
<div className={styles.details_container}>
<h3>{vid.snippet.title}</h3>
</div>
</div>
);
})}
</div>
</div>
<div>
<Modal
open={modalIsOpen}
onClose={() => setModalIsOpen(false)}
center
classNames={{
overlay: 'customOverlay',
modal: 'customModal',
overlayAnimationIn: 'customEnterOverlayAnimation',
overlayAnimationOut: 'customLeaveOverlayAnimation',
modalAnimationIn: 'customEnterModalAnimation',
modalAnimationOut: 'customLeaveModalAnimation',
}}
animationDuration={800}
>
<ReactPlayer
playing={true}
url={videoURL}
width='100%'
height='calc(100vh - 100px)'
config={{
youtube: {
playerVars: {
autoplay: 1,
controls: 1,
},
},
}}
/>
</Modal>
</div>
<Footer />
</>
);
}
export async function getStaticProps() {
const keyword = useStore((state) => state.keyword);
const { YOUTUBE_KEY } = process.env;
const uploadsURL = `https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelId=UCbqKKcML7P4b4BDhaqdh_DA&maxResults=50&key=${YOUTUBE_KEY}&q=${keyword}`;
async function getData() {
const uploadsData = fetchData(uploadsURL);
return {
videos: await uploadsData,
};
}
const { videos } = await getData();
return {
revalidate: 86400,
props: {
videos: videos.items,
},
};
}
Would someone please help me out by telling me what I did wrong and how it works in the right way? Thank you guys!!

How to match an element with data clicked in react hooks?

I am trying to create a modal that will display all the data on the element clicked.
For exemple, if i clicked the id(367) of a movie, it will display the title, the genres and the released date.
First i created a Context.js that will send data in the component Movie.js:
Context.js
import React, { useState, useEffect, createContext } from "react";
import { getAxios } from "../Function/index";
const MovieContext = createContext();
const MovieProvider = ({ children }) => {
const [datas, setDatas] = useState([]);
const [testdatas, testsetDatas] = useState([]);
const getMovie = async () => {
const data = await getAxios(
"https://api.themoviedb.org/3/movie/upcoming?api_key={}"
);
setDatas(data.results);
};
useEffect(() => {
getMovie();
}, []);
return (
<MovieContext.Provider value={{ datas, testdatas }}>{children}</MovieContext.Provider>
);
};
export { MovieContext, MovieProvider };
Movie.js
import React, { useContext } from "react";
import { MovieContext } from '../Context'
import { Card, Row, Tag } from 'antd'
const Main = () => {
const { datas } = useContext(MovieContext)
return (
<Row gutter={16}>
{datas.map((item, id) => (
<Card
key={id}
style={{ width: 300, margin: 10 }} bordered={true}
hoverable
>
<div>
<p style={{ textAlign: "left" }}>{item.title} <span style={{ float: "right" }}>{item.vote_average}</span></p>
</div>
<p><img src={`https://image.tmdb.org/t/p/w500/${item.poster_path}`} alt="#" width="200" height="200" /></p>
<Tag color="red"> {item.title}</Tag>
</Card>
))}
</Row >
);
};
export default Main;

React saving rate in state and localStorage

I have some trouble with my code. I made an app where I use an API last fm and I want to add a rating button, I get few things from Google. Rating is displayed where I want to be, I can console log him, but it's on external file and I have no idea how to modify rate state from my app.js. Here is my code:
App.js
import React, { Component } from 'react';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { getArtists } from './services/api';
import {
TextField,
Button,
List
} from '#material-ui/core';
import { ArtistCard } from './components/ArtistCard';
import { SearchResult } from './components/SearchResult';
import './App.css';
import { get } from 'https';
const handleChangeRate = (state) => {
this.setState({rate: state})
}
const isEmpty = (str) => str.length === 0;
class App extends Component {
state = {
searchTerm: '',
savedArtists: [],
rate:[]
}
componentDidMount() {
const existing = localStorage.getItem('savedArtists')
if (existing) {
this.setState({ savedArtists: JSON.parse(existing) })
}
}
onTextChange = (event) => {
const value = event.target.value;
this.setState({ searchTerm: value });
}
search = async (terms) => {
const artists = await getArtists(terms);
this.setState({ artists: artists })
}
onSearchClick = () => {
this.search(this.state.searchTerm);
}
clearSearch = () => {
this.setState({
searchTerm: '',
artists: []
})
}
updateArtists = (newArtists) => {
this.setState({ savedArtists: newArtists })
localStorage.setItem('savedArtists', JSON.stringify(newArtists));
}
deleteArtist = (artist) => {
const result = this.state.savedArtists.filter(item => item.name !== artist.name);
this.updateArtists(result);
}
onResultClick = (artist) => {
this.clearSearch();
const savedArtists = this.state.savedArtists;
savedArtists.push(artist);
this.updateArtists(savedArtists);
}
handleChangeRate = (state) => {
this.setState({rate: state})
}
render() {
const results = this.state.artists || [];
return (
<div className="App">
<header className="App-header">
<AppBar position="static" color="primary">
<Toolbar className="search-bar">
<Typography variant="h6" color="inherit">
Photos
</Typography>
<TextField
placeholder="Search on Last.fm"
className="search-input"
onChange={this.onTextChange}
value={this.state.searchTerm}
/>
<Button
onClick={this.onSearchClick}
variant="contained"
color="secondary"
disabled={isEmpty(this.state.searchTerm)}
>
Search
</Button>
{!isEmpty(this.state.searchTerm) && (
<Button
onClick={this.clearSearch}
variant="contained"
>
Clear
</Button>)
}
</Toolbar>
</AppBar>
</header>
<List className="search-results">
{
results.map((artist, index) => {
return <SearchResult key={index} artist={artist} onResultClick={this.onResultClick} />
})
}
</List>
<div className="artist-container">
{
this.state.savedArtists.map((artist, index) => {
return <ArtistCard artist={artist} key={index} deleteArtist={this.deleteArtist} onChangeRating={this.handleChangeRate} />
})
}
</div>
</div>
);
}
}
export default App;
artistCard.js
import React from 'react';
import { Card, CardContent, CardActions, Button } from '#material-ui/core'
import ReactStars from 'react-stars'
export const ratingChanged = (newRating) => {
const { onChangeRating } = this.props;
onChangeRating(newRating);
}
export const ArtistCard = (props) => {
const { artist, deleteArtist } = props;
console.log(artist.cardImage)
return (
<Card className="artist-card">
<div className="image-container">
<img src={artist.cardImage} alt={artist.name} />
</div>
<CardContent>
<h3>{artist.name}</h3>
<p>{artist.listeners} listeners.</p>
<ReactStars
count = {5}
onChange={ratingChanged}
size={27}
color2 ={'#ffd700'}
/>
</CardContent>
<CardActions>
<Button size="small" color="primary">
Share
</Button>
<Button size="small" color="secondary" onClick={() => deleteArtist(artist)}>
Delete
</Button>
</CardActions>
</Card>
)
}
You need to pass the function to change State to artistCard as props
In App.js add the following fucntion
const handleChangeRate = (state) => {
this.setState(rate: state)
}
and Pass the same as props to ArtistCard like specified
<ArtistCard artist={artist} key={index} deleteArtist={this.deleteArtist} onChangeRating={this.handleChangeRate} />
And in artistCard.js change ratingChanged method to
const ratingChanged = (newRating) => {
const { onChangeRating } = this.props;
onChangeRatng(newRating);
}
PS: This answer is based on the understanding i gained after going through this question, If this is not the requirement Please feel free to comment
EDIT
const handleChangeRate = (state) => {
this.setState(rate: state)
}
try adding the value prop to ReactStart like specified below
<ReactStars
value={this.props.rate}
count={5}
onChange={ratingChanged}
size={24}
color2={'#ffd700'}
/>
Pass rate state to artist card component as prop like specified below
<ArtistCard artist={artist} key={index} deleteArtist= {this.deleteArtist} onChangeRating={this.handleChangeRate} rate={this.state.rate} />
EDIT
cardComponent.js
import React from 'react';
import ReactStars from 'react-stars'
export const ArtistCard = (props) => {
const { artist, deleteArtist, onChangeRating } = props;
console.log(props.rating)
return (
<ReactStars
value={props.rating}
count = {5}
onChange={(newRating) => onChangeRating(newRating)}
size={27}
color2 ={'#ffd700'}
/>)}
App.js
handleChangeRate = (state) => {
this.setState({rate: state})
}
<ArtistCard artist={'artist'} key={'index'} rating={this.state.rate} deleteArtist={this.deleteArtist} onChangeRating={this.handleChangeRate} />
FINAL EDIT
Changes made in your code
App.js
modified state object to
state = {
searchTerm: '',
savedArtists: [],
rate: ''
}
Artistcard component line to
<ArtistCard rate={this.state.rate} artist={artist} key={index} onChangeRating={(val) => {this.setState({ rate: val })}} deleteArtist={this.deleteArtist} />
In ArtistCard.js
rendering reactstart component like this
<ReactStars
value={props.rate}
count = {5}
onChange={(newRating) => onChangeRating(newRating)}
size={27}
color2 ={'#ffd700'}
/>

Resources