Search bar searching combined API React - javascript

Im making a small school project with a react frontend showing different characters from tv shows/movies.
I printed these chars out into some tabels, fetched from 3 different APIs (Harry Potter, Star Wars and GoT)
On the frontpage of the site i want to have a search bar function where you can search for characters in all 3 apis and show them in a table/modal (doesnt matter for now) Therefor i combined the 3 APIs into one endpoint containing them all in an array
Im having some troubles making this search bar function. I want to have a search bar where can write part of the name of a character and it will suggest or show a character based on that (ex. Harr should show/suggest Harry Potter)
So far my code looks like this:
export default function Search({ searchingForChar }) {
const init = [{ name: "", fullName: "" }];
const [allCharacters, setAllCharacters] = useState(init);
const fetchData = () => {
console.log("test");
if (searchingForChar !== "" || searchingForChar !== undefined) {
searchFacade
.searchForAllChars()
.then((data) =>
setAllCharacters([
...data.swList.results,
...data.hpList.hpDTOList,
...data.gotList.results,
])
);
}
};
useEffect(fetchData, [searchingForChar]);
return (
<>
{allCharacters
.filter((char) => char.name.includes(searchingForChar))
.map((filteredPerson) => (
<>
<h1>Her burde stå et navn: {filteredPerson.name}</h1>
</>
))}
</>
);
}
And is used in the Form here:
export default function Home() {
const [value, setValue] = useState(),
onInput = ({ target: { value } }) => setValue(value),
onFormSubmit = (e) => {
e.preventDefault();
console.log(value);
setValue();
};
return (
<div className="container-fluid padding">
<img className="logo" src={Dachma} alt=""></img>
<div className="row">
<div className="col-3"></div>
<div className="col-6 text-center">
<h4 className="mt-5">Search for your favorite character</h4>
<Form onSubmit={onFormSubmit}>
<Form.Control onChange={onInput} placeholder="Search here.." />
<Form.Text className="text-muted">
Type character name, movie, tv show etc.
</Form.Text>
<MDBBtn
outline
color="primary"
rounded
size="m"
type="submit"
className="mr-auto"
>
Search
</MDBBtn>
</Form>
<Search searchingFor={value} />
</div>
<div className="col-3"></div>
</div>
<div className="row">
<div className="col-2"></div>
<div className="col-8">
<h4 className="mt-5 text-center">Current shows in the site:</h4>
<p className="mt-2 text-muted text-center">
Click to get info about a movie/tv show.
</p>
<div className="flexDirection: row justifyContent: space-between">
<Card imgToDisplay={gotImg} />
<Card imgToDisplay={hpImg} />
<Card imgToDisplay={swImg} />
</div>
</div>
<div className="col-2"></div>
</div>
</div>
);
}
I have tried many different ways found here on stack and other places, i just cant seem to getting it to work as intended
Ive had many different errors but for now im stuck on "Cannot read property 'includes' of undefined.
Can anyone point me in the right direction? Please know that im a beginner to react/JS, so please explain if you can help.

Related

How to preview posts on single page

Hi,
this is my code in react js
I want to show my blog posts on single page on clicking read more button I pick this url from
news api and i want to show every pot by its specific id but i use url as id
import React, { Component } from "react";
import BlogPage from "./BlogPage";
import propTypes from "prop-types";
import InfiniteScroll from "react-infinite-scroll-component";
export default class blogPost extends Component {
static defaultProps = {
category: "general",
};
static propTypes = {
category: propTypes.string,
};
constructor(props) {
super(props);
this.state = {
articles: [],
};
/* document.title = `${this.props.category}-news `; */
}
async componentDidMount() {
let url =
let data = await fetch(url);
let parsData = await data.json();
console.log(parsData);
this.setState({
articles: parsData.articles,
totalResults: parsData.totalResults,
});
}
getMorePost = async () => {
let url =
let data = await fetch(url);
let parsData = await data.json();
console.log(parsData);
this.setState({ articles: this.state.articles.concat(parsData.articles) });
};
render() {
return (
<div>
<div className="container">
{/* <span style={{ color: "grey" }}>
Blogs
<span style={{ fontWeight: "500", color: "black" }}>
- {this.props.category}
</span>
</span> */}
<InfiniteScroll
dataLength={this.state.articles.length}
next={getMorePost}
hasMore={this.state.articles.length !== this.state.totalResults}
loader={<h3> Loading...</h3>}
endMessage={<h4>Nothing more to show</h4>}
>
<div className="row pt-3.">
{this.state.articles.map((element) => {
return (
<div className={this.props.BlogClass} key={element.url}>
<BlogPage
CardClass={this.props.ClassCard}
ClassDes={this.props.DesClass}
ClassTitle={(this.props.TitleClass, " heading-blog")}
CardBody={this.props.BodyCard}
imgStyle={this.props.styleImg}
CardStyle={{
width: "100%",
height: "100%",
padding: "10px 0",
border: "none",
}}
title={element.title ? element.title.slice(0, 45) : ""}
description={
element.description
? element.description.slice(0, 90)
: ""
}
author={
/* element.author? element.author.slice(0, 5) : "" */
element.category
}
img={element.urlToImage}
/>
</div>
);
})}
</div>
</InfiniteScroll>
</div>
</div>
);
}
}
I want to show this posts on this page by clicking the read more button i try it several times but don't understand it
import React, { useState, useEffect } from "react";
import BlogPost from "./BlogPost";
export default function Blogpreview() {
/* const [blog,setBlog]=useState()
useEffect(() => {
let blog = articles.find((blog) => blog.id === parseInt(id));
if (blog) {
setBlog(blog);
}
}, []) */
/* const [blog,setBlog]=useState(null)
useState = async () =>{
let url =
let data = await fetch(url);
let parsData = await data.json();
console.log(parsData);
setBlog({ articles: parsData.articles,
totalResults: parsData.totalResults
});
}
*/
return (
<div className="container-fluid">
<div className="col-6 BlogPreviewBox px-5 ">
<div className="d-flex justify-content-center">
<img className="display-blog-box my-5" src="/"></img>
</div>
<h1 className="blog-preview-heading">
Why invoicing are beneficial for your business{/* {blog.title} */}
</h1>
<div className="d-flex flex-column align-items-center">
<span className="text-center py-3">07 December 2021</span>
<button className="btn-blog-display">2 min read</button>
</div>
</div>
<div className="row display-blog-content-row">
{/* <div className="col-3">
{ <div>
<p >What is Olabooks?</p>
<div className="d-flex flex-column">
<span>How to add invoice in Olabooks?</span>
<span className="py-2">How to share invoice in Olabooks?</span>
</div>
<i className="far fa-heart"></i>
</div>}
</div> */}
<div className="col-6">
<div className="d-flex flex-column display-blog-text">
<div>
<span>
Businesses that adopt advanced technologies are more likely to
thrive in the long run because of the rapid response to modern
techniques and innovation. Many questions need to be taken into
account while adopting advanced technology, but most
importantly, you begin to think about the values it must provide
to you and your business. It is not an easy task toruna
business, especially in this tech-savvy world. Let's dig into
some commercial benefits of moving from pen and paper invoicing
to efficient e-invoicing that would make headway for your
business.
</span>
</div>
</div>
<div className="tag-display-blog">
<div>
<strong>By Olabooks</strong>
<span>author</span>
</div>
</div>
</div>
{/* <div className="col-3">
<div>
<span>Related</span>
</div>
<div className="displa-related-blog">
<h5 className="py-2">Why is e-invoicing beneficial for your business?</h5>
<span>
Business that adopt advanced technologies are more likely to thrive in the long run because of
</span>
<button className="btn-related-blog-display">9 min read</button>
</div>
</div> */}
</div>
</div>
);
}
I tried making API calls to the same URL, it seems that you are getting same API response in both cases (with and without pageSize param), due to which, your "read more" is actually not rendering any new content to the screen
(with the pageSize query parameter)
(without the pageSize parameter)
Also, it is better to not include your API key in the question

How can I prevent TypeError: Cannot read property 'map' of undefined when submitting empty form data?

Ok so my code works great but when I hit submit while the input field is empty my .map method doesnt have a input value to add to the end of the API string to search for so I get a typeError: cannot read property of map undefined. because input is my state and if I dont enter a string into the input field the state doesnt get updated so the .map method has no updated state to go off of.
SO how would I set the input field to require text to be entered before the submit button will even call the function "SearchApi"?
I was trying to set conditionals but it didnt seem to work....like....if input.length < 1 { setInput("E.T") just so it has something to search for and doesn't return an error. I also was looking online at react form validation tutorials but I feel like maybe i'm going about this all wrong. Any help would be appriciated thank you!
import React, {useState, useEffect} from 'react';
const SearchBar = () => {
const [search, setSearch] = useState([]);
const [input, setInput] = useState('');
const [trending, setTrending] = useState([]);
const [upcoming, setUpcoming] = useState([]);
// Input Field
const onUserInput = ({target}) => {
setInput(target.value);
}
// Api Call
const SearchApi = (event) => {
const aUrl = "https://api.themoviedb.org/3/search/movie?api_key=fde5ddeba3b7dec3fc1f51852ca0fb95";
const newUrl = aUrl +'&query=' + input;
event.preventDefault();
fetch(newUrl)
.then((response) => response.json())
.then((data) => {
setSearch(data.results);
})
.catch((error) => {
console.log('Error!! Data interupted!:', error)
})
}
return (
// Heading
<div>
<div className="container">
<h1>Movie Search Extravaganza!</h1>
{/* Input Field and Button Form */}
<form>
<input value={input} onChange={onUserInput} type="text" className="searchbar" aria-label="searchbar" placeholder="search" name="movie" required/>
<br></br>
<button type="submit" onClick={SearchApi} aria-label="searchbutton" className="searchBtn">Movie Express Search</button>
</form>
</div>
<div className="byName-container">
<h1 className="row-label" tabIndex="0">Movies Related To Your Search</h1>
<ul className="flexed-search">
{search.map((item) =>
<div className="poster-container">
<li className="list-item" key={item.id}>
<img className="image-element" tabIndex="0" alt="movie poster" aria-label={item.title} src={`https://image.tmdb.org/t/p/w500${item.poster_path}`} />
<h3 className="posterTitle">{item.title}</h3>
</li>
</div>
)}
</ul>
</div>
<div className="trending-container">
<h1 className="row-label" tabIndex="0">This Weeks Trending Tittles</h1>
<ul className="flexed-trending">
{trending.map((it) =>
<div className="poster-container">
<li className="list-item" key={it.id}> <img className="image-element" tabIndex="0" aria-label={it.title} alt="movie poster" src={`https://image.tmdb.org/t/p/w500${it.poster_path}`} />
<h3 className="posterTitle">{it.title}</h3>
</li>
</div>
)}
</ul>
</div>
<div className="upcoming-container">
<h1 className="row-label" tabIndex="0">Upcomming Movies</h1>
<ul className="flexed-upcoming">
{upcoming.map((inn) =>
<div className="poster-container">
<li className="list-item" key={inn.id}>
<img className="image-element" tabIndex="0" alt="movie poster" aria-label={inn.title} title={`${inn.title}: ==> ${inn.overview}`} src={`https://image.tmdb.org/t/p/w500${inn.poster_path}`} />
<h3 className="posterTitle">{inn.title}</h3>
</li>
</div>
)}
</ul>
</div>
</div>
)
};
export default SearchBar;```
You made a small mistake in your form
Change this :
<form>
...
<button type="submit" onClick={SearchApi} aria-label="searchbutton" className="searchBtn">
Movie Express Search
</button>
</form>
By :
<form onSubmit={SearchApi}>
...
<button type="submit" aria-label="searchbutton" className="searchBtn">
Movie Express Search
</button>
</form>
The "required" of the input will work. Demo: Stackblitz
PS : Check the keys in all your map functions
{search.map(item => (
<div className="poster-container"> //pass the key here and do the same for others
<li className="list-item" key={item.id}>
you could try to make required explicit with
required={true}

Typerror: object is not a function or its return value is not iterable in react 16.13.1

I am using react version 16.13.1. And i have used hooks alot of time in my project.but now am getting this error even before i use it.
export default function TourData(props) {
const [collapsed, setCollapsed] = useState();
const withinDatacollapse = props.tourWithin.slice(1);
const withinDatashow = props.tourWithin.slice(0, 1);
function handleViewAll() {
setCollapsed(!collapsed);
}
function Object(props) {
return (
<div>
{props.data &&
props.data.map((item, index) => (
<div key={index} className="within">
<div
className="image"
style={{ background: `url(${item.image})` }}
>
<div className="destination">{item.country}</div>
</div>
<div className="options">
<div>
<h5>Options</h5>
<div className="user-select">
<Select
options={item.options}
placeholder={<h4>Select Option</h4>}
/>
</div>
</div>
</div>
<div className="date-select">
<div className="user-select">
<Select
options={item.date_options}
placeholder={<h4>Select Date</h4>}
/>
</div>
</div>
<div>
<button>Go!</button>
</div>
</div>
))}
</div>
);
}
return (
<div className="route">
<div className="list-box">
<Object data={withinDatashow} />
<Collapse in={collapsed}>
<Object data={withinDatacollapse} />
</Collapse>
<div className="bottom-view-section">
<button onClick={handleViewAll}>View All</button>
</div>
</div>
</div>
);
}
Here i just declared the hook, and am getting this error.
I cant find where i have went wrong.
At first i had an error TypeError: Cannot read property 'map' of undefined where i do mapping , so i had to put props.data && just before the mapping.
You shouldn't name your components as Javascript's keywords like Object or class.
Also, you might want to define default value for props.placeData so when it is undefined, you don't get errors.
export default function AppMain({placeData = [], ...props}) {
Or you can just check in return, and say something like 'there is no place'. It's up to you.

Queried data disappears when webpack initially loads, then appears when I go to other page on website and return

Can anyone explain what's going on here?
On the index page referenced below there is a section where I source data from a WordPress API to generate the four most recent posts from a client. When running in develop mode with Gatsby, the website is presented fine, but when I upload the website to Netlify and build it out, the data disappears and leaves nothing more than an empty set of p tags.
When I go into development tools and step through breakpoints, I notice that the data in question appears on the website, but then disappears once the webpack fires and the modules are bundled. It's almost as if this data is getting overwritten. When I navigate away from this page on the same website, and then return, the p tags HAVE the data in question. I'm assuming the webpack overwrites the initial code, and then when I come back to the page the webpack has already fired so it loads the information fine? How do I work around this? Excuse me if this is a silly/obvious question.
Full code in reference:
import React from "react"
import { Link, graphql, useStaticQuery } from 'gatsby'
import Layout from '../components/layout'
import indexStyles from '../components/modules/index.module.css'
import Carousel from 'nuka-carousel'
import header1 from '../img/header1.jpg'
import header2 from '../img/header2.jpg'
import header3 from '../img/header3.jpg'
const IndexPage = () => {
const data = useStaticQuery(graphql`
query {
allWordpressPost (sort: {fields:date, order:DESC}) {
edges {
node {
title
slug
excerpt
date(formatString:"MMMM DD, YYYY")
}
}
}
}
`)
return (
<Layout>
<div className={indexStyles.indexCarousel_container}>
<Carousel
autoplay={true}
autoplayInterval={5000}
pauseOnHover={false}
wrapAround={true}
renderCenterLeftControls={({ previousSlide }) => (
<button onClick={previousSlide} className={indexStyles.indexCarousel_button}><i className="fas fa-arrow-left"></i></button>
)}
renderCenterRightControls={({ nextSlide }) => (
<button onClick={nextSlide} className={indexStyles.indexCarousel_button}><i className="fas fa-arrow-right"></i></button>
)}>
<div className={indexStyles.indexCarousel_slideContainer}>
<img src={header1} alt="Pencil case with cat, heart, and cupcake design."></img>
<div>
<h2>Shop</h2>
</div>
</div>
<div className={indexStyles.indexCarousel_slideContainer}>
<Link to="/blog"><img src={header2} alt="Notepad next to a cup of coffee."></img></Link>
<div>
<h2>Blog</h2>
</div>
</div>
<div className={indexStyles.indexCarousel_slideContainer}>
<img src={header3} alt="Colorful pencil cases."></img>
<div>
<h2>Cute Castle VIP</h2>
<p>Save 20%!</p>
</div>
</div>
</Carousel>
</div>
<h1 className={indexStyles.indexHeader}>Latest Posts</h1>
<div className={indexStyles.indexPost_container}>
<div className={indexStyles.indexPost_container}>
{data.allWordpressPost.edges.map((edge, i) => {
if (i < 4) {
return (
<div className={indexStyles.index_post}>
<h2><Link to={`/blog/${edge.node.slug}`} className={indexStyles.post_title} dangerouslySetInnerHTML={{ __html: edge.node.title }}></Link></h2>
<p className={indexStyles.post_date}>{edge.node.date}</p>
<p className={indexStyles.post_excerpt} dangerouslySetInnerHTML={{ __html: edge.node.excerpt }} />
<p><Link to={`/blog/${edge.node.slug}`} className={indexStyles.post_link}>Read more</Link></p>
</div>
)
}
})}
</div>
</div>
</Layout >
)
}
export default IndexPage
The section of code that disappears and reappears:
<div className={indexStyles.indexPost_container}>
<div className={indexStyles.indexPost_container}>
{data.allWordpressPost.edges.map((edge, i) => {
if (i < 4) {
return (
<div className={indexStyles.index_post}>
<h2><Link to={`/blog/${edge.node.slug}`} className={indexStyles.post_title} dangerouslySetInnerHTML={{ __html: edge.node.title }}></Link></h2>
<p className={indexStyles.post_date}>{edge.node.date}</p>
<p className={indexStyles.post_excerpt} dangerouslySetInnerHTML={{ __html: edge.node.excerpt }} />
<p><Link to={`/blog/${edge.node.slug}`} className={indexStyles.post_link}>Read more</Link></p>
</div>
)
}
})}
</div>
</div>
A link to the Netlify project.
https://zealous-engelbart-509321.netlify.com/
Thanks in advance for your help!

How to update upvote counter for individual elements instead of all of them at once with React

Newbie dev learning React.
I'm trying to create an upvote functionality to a blog post in React but when I click on the upvote button I'm upvoting all of the blog post cards at once instead of the individual card.
How can I fix this? I believe the issue may be in the way I'm setting setState? But I may be wrong and looking for help.
Thanks in advance!
====
class Posts extends Component {
state= {
points: 0
}
componentDidMount() {
this.props.fetchPosts()
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render() {
const postItems = this.props.posts.map((post, index) => (
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{post.title}</div>
<div className="meta"> {post.author}</div>
<div className="description">
<p>{post.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
))
return (
<div>
<br />
<h2 className="ui header">
<i className="pencil alternate icon"></i>
<div className="content">
Blog Feed
<div className="sub header">Create New Post!</div>
</div>
</h2>
{postItems}
</div>
)
}
}
You have a single component storing the "points" state for all your posts. To achieve the functionality you described, each post should be it's own component with it's own state.
class Post extends Component {
state = {
points: 0
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render = () =>
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{this.props.title}</div>
<div className="meta"> {this.props.author}</div>
<div className="description">
<p>{this.props.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
}
}
You are upvoting every card because you have only one counter. A separate counter should be defined for every card.
state = {}; // dictionary-a-like state structure
handleClick = (id) => () => {
this.setState((prevState) => ({
[id]: prevState[id] ? prevState[id] + 1 : 1, // check and increment counter
}));
}
onClick={this.handleClick(post.id)} // call function with post.id as argument
{this.state[post.id] || 0} Votes // display votes for every card
Note: I assumed that every card has it's own unique id, if not - index may come handy too.
You will need one counter for each post. Currently you only have a single counter for all posts, which means that they all display that same value.
The best way to achieve this would probably be to separate your post into its own component, and have that keep track of the counter.
The following solution uses a post ID (if you have it) to create a key in a stateful points object. Then, on click, you can add to the correct points key.
state = {
points: {}
}
handleClick = postId => {
this.setState({
points: {
...this.state.points,
[postId]: (this.state.points[postId] || 0) + 1
}
})
}
const postItems = this.props.posts.map((post, index) => (
...
<div className="extra content">
<i className="check icon"></i>
{this.state.points[post.id] || 0} Votes
</div>
<button
className="ui button"
type="submit"
onClick={() => this.handleClick(post.id)}
>
Add Point
</button>
...
)

Categories