How to implement API search onClick - javascript

My project is using React, Axios, and movieDB's API. I am trying to make it so that when I type out a movie title and either hit enter and/or click submit then the API will return the title of the movie searched for as an h1 element.
currently, the code works as soon as I refresh the browser and the only way for it to function properly is if I go into the code and replace ${query} with what I want to search for, ie joker. I have tried adding the onclick to the button : <button onclick="componentDidMount()">submit</button>. This did not do anything, however.
App.js
import React from "react"
import Movielist from './components/Movielist'
function App() {
return (
<div>
<input type="search" />
<button onclick="componentDidMount()">submit</button>
<h1 id="title">title</h1>
<Movielist />
</div>
)
}
export default App
Movielist.js
import React from 'react';
import axios from 'axios';
export default class Movielist extends React.Component {
state = {
title: ""
}
componentDidMount() {
const API_KEY = '*****************';
const query = document.getElementById('search');
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`)
.then(res => {
const title = res.data['results'][0]['title'];
this.setState({ title });
})
}
render() {
return (
<h1>{this.state.title}</h1>
)
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);

You have a few things wrong here:
The structure of your app is not great, eg. seperate out your API calls
You are calling a lifecycle method, these are called automatically
Don't use DOM selectors like getElementById in React
Use camelcase event listeners (onclick should be onClick)
Use callbacks with event listeners or they will fire immediatly
You included your API key on stackoverflow, big mistake!
Try this:
https://codepen.io/alexplummer/pen/YzwyJOW
import React, {useState} from "react";
const MovielistSearch = props => (
<>
<input type="search" onChange={e => props.saveSearchInput(e.target.value)} />
<button type="button" onClick={() => props.handleSearch()}>
Search
</button>
</>
);
const getMovies = props => {
return ['Title 1', 'Title 2', 'Title 3'];
// ADD REAL API HERE
// const API_KEY = '';
// const getMovies = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${props.searchTerm}`);
// return getMovies.data;
}
const MovieList = props => (
<ul>
{props.foundMovies.map(thisMovie=><li>{thisMovie}</li>)}
</ul>
);
const App = () => {
const [searchInput, setSearchInput] = useState("");
const [foundMovies, setFoundMovies] = useState([]);
const movieSearch = ()=> {
if (searchInput == null) return;
const foundMovies = getMovies(searchInput);
setFoundMovies(foundMovies);
}
return (
<div>
<h1 id="title">Movie list</h1>
<MovielistSearch saveSearchInput={setSearchInput} handleSearch={movieSearch} />
<MovieList foundMovies={foundMovies} />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))

The problem might be here:
const query = document.getElementById('search');
It returns an HTML element. Try document.getElementById('search').innerText

Related

TypeError: v.setActiveFile is not a function

I'm creating my project in ReactJS. In this projects, there is the page into I should make a textarea. This page is divided in two part, the sidebar, where you see all the files of the current workspace, and the editor, an html textarea. When I click a button, I would like that the current file changes. So, I created a context to pass to the child element. Here is the code of the Parent component, we can call it:
import '../styles/style.css';
import Editor from './Editor'
import Sidebar from './Sidebar'
import React, {useState} from 'react';
const WContext = React.createContext();
const EditPage = () => {
const {activeFile, setActiveFile} = useState('Empty');
return <>
<WContext.Provider value={{activeFile, setActiveFile}}>
<h1 className="edittitle">Edit Page</h1>
<div className="edit">
<Sidebar />
<Editor />
</div>
</WContext.Provider>
</>
}
export default EditPage;
export {WContext};
Then, there is the sidebar code:
import {useEffect, useState, useContext} from 'react';
import {WContext} from './EditPage';
import axios from 'axios';
const Sidebar = () => {
const v = useContext(WContext);
const [file, setFile] = useState([]);
useEffect(() => {
//request to server
}, [])
return <>
<div className="sidebar">
<h3>Files of workspace</h3>
<div className="sidesaparator"></div>
{
file.map(f=>{
return <button key={f._id} onClick={() => v.setActiveFile(f)} style={{'padding':"10px"}}>
{f.name + "." + f.extension}</button>
})
}
</div>
</>
}
export default Sidebar;
Finally, the editor code:
import sun from '../images/sun.png';
import moon from '../images/moon.png';
import {useState, useContext} from 'react';
import {WContext} from './EditPage';
const Editor = () => {
const v = useContext(WContext);
const [text, setText] = useState('//Your code');
const handleChanges = (e) => {
setText(e.target.value);
setLine(countLine);
}
return <>
<form className="editor">
<div className="opened"> {v.activeFile} </div>
<textarea name="code" value={text} className="editorText" onChange={handleChanges} spellCheck="false"
id = {bg}></textarea>
</form>
</>
}
export default Editor;
In this last file, the div with className "opened" should have as text the value of activeFile, but it is an empty string. On the other hand, in the previus file, when I click in one file, it should become the active one, but the error in the question title appears. I would like to know where I'm mistaking, and how to fix it. Thank you so much
All this can be fixed by switching from brakets to square parentesis the useState declaration

NextJS: Pass string via context from input into getStaticProps

I´m new to NextJS and React at all so I ask for your forgiveness.
I want to know how to pass an users written text from an input field (inside of Header) into the getStaticProbs function of a specific page via the react context api.
I tried the following source but it doesn`t work - it throws out an error that my way to build leads to an invalid hook call.
Here is my context source:
import React, { createContext, useState } from 'react';
export const SearchContext = createContext();
export const SearchProvider = ({ children }) => {
const [keyword, setKeyword] = useState('');
return (
<SearchContext.Provider
value={{
keyword,
setKeyword,
}}
>
{children}
</SearchContext.Provider>
);
};
to fetch the written string of SearchBar.js:
import React, { useContext, useState } from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image';
import loupe from '../public/images/loupe.png';
import { SearchContext } from '../lib/searchCtx';
const SearchBar = () => {
const search = useContext(SearchContext);
const router = useRouter();
const submitAction = (e) => {
e.preventDefault();
router.push(`/searchResults`);
};
return (
<div className={styles.searchBar}>
<input
type='text'
placeholder='Suche...'
onChange={(e) => search.setKeyword(e.target.value)}
/>
<button className={styles.searchBtn} type='submit' onClick={submitAction}>
<Image src={loupe} alt='' />
</button>
</div>
);
};
export default SearchBar;
and pass it over _app.js:
import Header from '../components/Header';
import Footer from '../components/Footer';
import { SearchProvider } from '../lib/searchCtx';
function MyApp({ Component, pageProps }) {
return (
<>
<SearchProvider>
<Header />
<Component {...pageProps} />
</SearchProvider>
<Footer />
</>
);
}
}
export default MyApp;
to get the value into getStaticProbs of searchResults.js:
import { useEffect, useState, useContext } from 'react';
import { fetchData } from '../lib/utils';
import styles from '../styles/Playlist.module.scss';
import Image from 'next/image';
import { SearchContext } from '../lib/searchCtx';
export default function SearchResults({ videos }) {
console.log(videos);
const sortedVids = videos
.sort((a, b) =>
Number(
new Date(b.snippet.videoPublishedAt) -
Number(new Date(a.snippet.videoPublishedAt))
)
)
return (
<>
<div className={`${styles.playlist_container} ${styles.search}`}>
<h1>Search results</h1>
{sortedVids
.map((vid, id) => {
return (
<div className={styles.clip_container}>
<Image
className={styles.thumbnails}
src={vid.snippet.thumbnails.medium.url}
layout='fill'
objectFit='cover'
alt={vid.snippet.title}
/>
</div>
<div className={styles.details_container}>
<h3>{vid.snippet.title}</h3>
</div>
);
})}
</div>
</>
);
}
export async function getStaticProps() {
const search = useContext(SearchContext);
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=${search.keyword}`;
async function getData() {
const uploadsData = fetchData(uploadsURL);
return {
videos: await uploadsData,
};
}
const { videos } = await getData();
return {
revalidate: 86400,
props: {
videos: videos.items,
},
};
}
Would you help me by 1) telling me the main failure I did and 2) providing me a working source?
How can I achieve it to get the keyword from SearchContext into the uploadsURL (inside of getStaticProbs) or isn`t it possible?
Thanks in advance!!
You can create a dynamic pages under your page folder one called say index.js and one called [slug].js (all under one folder) In the index page you can have your normal search input, when the users submit the query you can do
<a
onClick={() =>
router
.push(`/movies/${search.keyword}`)
.then(() => window.scrollTo(0, 0))}>
search
</a>
and in your [slug].js page you can retrieve that information like so
export async function getServerSideProps(pageContext) {
const pageQuery = pageContext.query.slug;
const apiCall= await fetch(
``https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelId=UCbqKKcML7P4b4BDhaqdh_DA&maxResults=50&key=${YOUTUBE_KEY}&q=${pageQuery}`
);
const results = await apiCall.json();
return {
props: {
data: results,
},
};
}
I don't know if this will work for you but is a solution

Using context API (React.js) to change only a particular key in state object

I am using context API in react for managing state. For this I have created a file AppContext.js where I have created context and Provider:
import { useState, createContext } from "react";
export const AppContext = createContext();
export const AppProvider = (props) => {
const [appdata, setAppdata] = useState({
data1: "this is data1",
data2: "this is data2",
});
return (
<AppContext.Provider value={[appdata, setAppdata]}>
{props.children}
</AppContext.Provider>
);
};
I have imported this Provider in the parent component of the app i.e App.js. Also I have wrapped the <AppChild/> component in the Provider.
import AppChild from "./AppChild";
import { AppProvider } from "./AppContext";
const App = () => {
return (
<AppProvider>
<div className="App">hello</div>
<AppChild />
</AppProvider>
);
};
export default App;
Now from AppChild component, I only needed to update the data1 key of my state. For this I have created a button with a onClick through which I will be changing my state. I have used to following code in AppChild.js for this:
import { useContext } from "react";
import { AppContext } from "./AppContext";
const AppChild = () => {
const [appdata, setAppdata] = useContext(AppContext);
return (
<div>
<h3 style={{ color: "red" }}>Data for Appchild: {appdata.data1}</h3>
<button
onClick={() =>
setAppdata((prev) => {
prev.data1 = "updated data1";
return prev;
})
}
>
click to change
</button>
</div>
);
};
export default AppChild;
But when I click the button, the text inside the <h3> block is not changing. Although when I change the whole state by passing the whole object directly to setAppdata as shown below,
This way the state updates successfully. Why is the first method not working where I only wanted to change the data1 key?
You are updating state in wrong way so it is not working. This is how you should update state
<button
onClick={() =>
setAppdata((prevState) => ({
...prevState,
data1: "Your New Data"
}))
}
>
click to change
</button>

React.js search bar always fetches the same content

I'm building a search engine with React.js, where I can look for GIPHY gifs using their API. Everytime I type a word(any word), it always loads the same gifs and when I erase and write another word, the gifs don't update.
index.js:
import React from 'react'; //react library
import ReactDOM from 'react-dom'; //react DOM - to manipulate elements
import './index.css';
import SearchBar from './components/Search';
import GifList from './components/SelectedList';
class Root extends React.Component { //Component that will serve as the parent for the rest of the application.
constructor() {
super();
this.state = {
gifs: []
}
this.handleTermChange = this.handleTermChange.bind(this)
}
handleTermChange(term) {
console.log(term);
let url = 'http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io';
fetch(url).
then(response => response.json()).then((gifs) => {
console.log(gifs);
this.setState({
gifs: gifs
});
});
};
render() {
return (
<div>
<SearchBar onTermChange={this.handleTermChange} />
<GifList gifs={this.state.gifs} />
</div>
);
}
}
ReactDOM.render( <Root />, document.getElementById('root'));
search.js
import React, { PropTypes } from 'react'
import './Search.css'
class SearchBar extends React.Component {
onInputChange(term) {
this.props.onTermChange(term);
}
render() {
return (
<div className="search">
<input placeholder="Enter text to search for gifs!" onChange={event => this.onInputChange(event.target.value)} />
</div>
);
}
}
export default SearchBar;
Giflist:
import React from 'react';
import GifItem from './SelectedListItem';
const GifList = (props) => {
console.log(props.gifs);
const gifItems = props.gifs && props.gifs.data && props.gifs.data.map((image) => {
return <GifItem key={image.id} gif={image} />
});
return (
<div className="gif-list">{gifItems}</div>
);
};
export default GifList;
GifItem:
import React from 'react';
const GifItem = (image) => {
return (
<div className="gif-item">
<img src={image.gif.images.downsized.url} />
</div>
)
};
export default GifItem;
I can't seem to find where is the issue here. Is it because of this.handleTermChange = this.handleTermChange.bind(this) and there is no "update" state after?
Any help is welcome :) Thanks!
Its because, you are not putting the term value entered by user in the url, all the time you hit the api with static value term, here:
'http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io';
Replace ' by ' (tick), like this:
let url = `http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io`;
Check MDN Doc for more details about Template Literals.

How to get event from components to container in React Redux

I'm new to Redux.
I handled the basic Facebook Flux architecture very easily and made some nice app with it.
But I struggle very hard to get very simple Redux App to work.
My main concern is about containers and the way they catch events from components.
I have this very simple App :
CONTAINER
import { connect } from 'react-redux'
import {changevalue} from 'actions'
import App from 'components/App'
const mapStateToProps = (state) => {
return {
selector:state.value
}
}
const mapDispatchToProps = (dispatch) => {
return {
onClick: (e) => {
console.log(e)
dispatch(changeValue())
}
}
}
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App)
export default AppContainer;
Component
import React, {Component} from 'react'
import Selector from 'components/Selector'
import Displayer from 'components/Displayer'
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/>
<Displayer />
</div>
)
export default App;
CHILD COMPONENT
import React, {Component} from 'react'
const Selector = ({onClick}) => (
<div onClick={onClick}>click me</div>
)
export default Selector;
onClick event does not reach the container's mapDispatchToProps.
I feel that if I get this work, I get a revelation, and finally get the Redux thing! ;)
Can anybody help me get this, please ? (The Redux doc is TOTALLY NOT helpfull...)
The problem is in the App component. In the onClick property of the Selector component, you're passing a function which returns the definition of a function, not the result.
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/> // here is the problem
<Displayer />
</div>
)
You should simply do this instead:
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick(e)}/>
<Displayer />
</div>
)
Or even simpler:
const App = (selector, onClick) => (
<div>
<Selector onClick={onClick}/>
<Displayer />
</div>
)

Categories

Resources