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.
Related
I'm not sure why my text area isn't rendering if someone could suggest what I'm missing or what I've done wrong I'd be forever grateful.
I'm not sure whether maybe I should try rending it in the App.js file.
I'm also sure I don't need to import the App.js file to the CardCheck.js because it's a child.
import "./App.css";
import React from "react";
import CardCheck from "./CardCheck";
class App extends React.Component() {
state = {
cardNumber: "",
};
handleChange = (event) => {
this.setState({ cardNumber: event.target.value });
};
handleClick = () => {
const { cardNumber } = this.state;
this.setState({
cardNumber: "",
});
};
render() {
const { cardNumber } = this.state;
return (
<div className="App">
<h1>Taken Yo Money</h1>
<CardCheck
cardNumber={cardNumber}
handleChange={this.handleChange}
handleClick={this.handleClick}
/>
</div>
);
}
}
export default App;
function CardCheck(props) {
const { cardNumber, handleChange, handleClick } = props;
return (
<div className="TweetInput">
<div className="bar-wrapper"></div>
<textarea onChange={handleChange} value={cardNumber}></textarea>
<footer>
<button onClick={handleClick}>Enter Card Details</button>
</footer>
</div>
);
}
export default CardCheck;
Remove the parentheses from class App extends React.Component(). It should be
class App extends React.Component {
//rest of the code
}
``
You need to replace class App extends React.Component() with class App extends Component {
The Component is imported from import React, { Component } from "react";
It should fix the rendering issue
I'm currently working on a react app exercise based around creating Spotify playlists. Here is the primary code-base:
App.js
import React from 'react';
import './App.css';
import SearchBar from'../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
const track = {
name: "Hello",
artist: "Again",
album: "Friend of a friend",
id: 0
};
const tracks = [track, track, track];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: tracks,
playlistName: "DEFAULT",
playlistTracks: tracks
};
this.addTrack = this.addTrack.bind(this);
this.removeTrack = this.removeTrack.bind(this);
}
addTrack(track) {
this.state.playlistTracks.map(id => {
if(track.id === id)
return;
});
this.setState((state, track) => ({
playlistTracks: state.playlistTracks.push(track)
}));
}
removeTrack(track) {
this.state.playlistTracks.map(id => {
if(track.id === id)
{
this.setState((state, track) => ({
playlistTracks: state.playlistTracks.remove(track)
}));
}
});
}
render(){
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults} onAdd= {this.addTrack}/>
<Playlist name= {this.state.playlistName} tracks= {this.state.playlistTracks} onRemove= {this.removeTrack}/>
</div>
</div>
</div>
);
}
}
export default App;
Playlist.js
import React from 'react';
import './Playlist.css';
import TrackList from '../TrackList/TrackList';
function Playlist(props) {
return (
<div className="Playlist">
<input value="New Playlist"/>
<button className="Playlist-save">SAVE TO SPOTIFY</button>
<TrackList tracks= {props.tracks} onRemove= {props.onRemove} isRemoval= {true}/>
</div>
);
}
export default Playlist;
import React from 'react';
import './SearchBar.css';
function SearchBar () {
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist" />
<button className="SearchButton">SEARCH</button>
</div>
);
}
export default SearchBar;
SearchBar.js
import React from 'react';
import './SearchBar.css';
function SearchBar () {
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist" />
<button className="SearchButton">SEARCH</button>
</div>
);
}
export default SearchBar;
SearchResults.js
import React from 'react';
import './SearchResults.css';
import TrackList from '../TrackList/TrackList';
function SearchResults (props) {
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList tracks={props.searchResults} onAdd= {props.onAdd} isRemoval= {false}/>
</div>
);
}
export default SearchResults;
Track.js
import React from 'react';
import './Track.css';
class Track extends React.Component {
constructor(props) {
super(props);
this.addTrack = this.addTrack.bind(this);
this.removeTrack = this.addTrack.bind(this);
}
addTrack() {
this.props.onAdd(this.props.track);
}
removeTrack() {
this.props.onRemove(this.props.track);
}
render() {
return (
<div className="Track">
<div className="Track-information">
<h3>{ this.props.track.name }</h3>
<p>{ this.props.track.artist } | { this.props.track.album }</p>
<div onClick= {this.addTrack}>+</div>
<div onClick= {this.removeTrack}>-</div>
</div>
</div>
);
}
}
export default Track;
TrackList.js
import React from 'react';
import './TrackList.css';
import Track from '../Track/Track';
function TracklList (props) {
return (
<div className="TrackList">
{
props.tracks.map(track => {
return <Track
track={track}
key={track.id}
onAdd= {props.onAdd}
onRemoval= {props.onRemoval}
isRemove= {props.isRemove}
/>;
})
}
</div>
);
}
export default TracklList;
It's a very baseline application at the moment but I'm trying to test a certain functionality so far. I am trying to test the process of adding a song from the Results section into the Playlist. However, when I click the plus icon on one of the songs, I get the following bug:
The bug only pops up after I click the plus icon so my initial assumptions is that I am changing the state object from an array to a list. however, I don't kow how that is the case. Anyway, I could definitely use a second opinion so far!
Array.push mutate the calling array and returns a number indicate the length of the new array.
In your addTrack method, your old playlistTracks state was mutated and new state was set to be a number instead of the new array.
Use the spread syntax to add new item to your array in an immutable way.
Also setState accept a new state, or a function that received old state and return new state, track should be received from the addTrack method
P/S: the .map above the setState seems like it won't work as intended.
addTrack(track) {
/*
* NOTE: the code below doesn't affect anything since `map`
* will be applied on each item and does not stop when you `return`
* also, it seems like `playlistTracks` is an array of object, but was
* used as an array of string here
*/
this.state.playlistTracks.map(id => {
if (track.id === id)
return;
});
this.setState((state/* ,track -- I think this should be removed */) => ({
// old code: playlistTracks: state.playlistTracks.push(track)
playlistTracks: [...state.playlistTracks, track]
}));
}
I am trying to concat the data entered in text field passing data from another stateless component, using props. Not sure why it is not working.
I have created two components
app.js 2. title.js
Data entered in input field needs to concat the string every time and display dynamically using props.
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Home from './Home';
import Title from './Title';
class App extends Component {
constructor(props)
{
super(props);
this.state =
{
text : ' ',
collection: []
}
this.eventHandler = this.eventHandler.bind(this);
this.eventSubmit = this.eventSubmit.bind(this);
}
eventHandler(event)
{
this.setState(
{text:event.target.value}
)
}
eventSubmit(event)
{
this.setState(
{collection:this.state.collection.concat(this.state.text)}
)
}
render() {
return (
<div className="App">
<input type="text" onChange ={this.eventHandler} />
<p> {this.state.text} </p>
<input type="submit" onClick={this.eventSubmit} />
<title collection={this.state.collection} />
</div>
);
}
}
export default App;
Title.js
import React from 'react';
const title = (props) =>
{
return (
<div>
<h1> {this.props.collection.toString()} </h1>
<h1> hello </h1>
</div>
);
}
export default title;
setState is async and when you use this.state inside it, it might not re-render. Use function inside setState instead:
eventSubmit(event) {
this.setState((prevState, props) => ({
collection: prevState.collection.concat(prevState.text)
}));
}
See 3. setState() is async: https://codeburst.io/how-to-not-react-common-anti-patterns-and-gotchas-in-react-40141fe0dcd
Mutations are bad in general and can lead to side effects use spread operator(...) to copy prevState array instead.
eventSubmit(event) {
this.setState((prevState) => ({
collection: [...prevState.collection, prevState.text]
}));
}
That's how you append data in array and update the state
Instead of stateless component I have created class component and it worked. Can someone explain me why it didn't worked with stateless why it worked now.
App.js
<code>
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Home from './Home';
import Title from './Title';
import Collection from './Collection';
class App extends React.Component {
constructor(props)
{
super(props);
this.state =
{
text : ' ',
collection: []
}
this.eventHandler = this.eventHandler.bind(this);
this.eventSubmit = this.eventSubmit.bind(this);
}
eventHandler(event)
{
this.setState(
{text:event.target.value}
)
}
eventSubmit(event)
{
this.setState(
{collection:this.state.collection.concat(this.state.text)}
)
}
render() {
return (
<div className="App">
<h1> ramesh </h1>
<input type="text" onChange ={this.eventHandler} />
<p> {this.state.text} </p>
<input type="submit" onClick={this.eventSubmit} />
<title name ={this.state.collection} />
<Collection name={this.state.collection} />
</div>
);
}
}
export default App;
</code>
Collection.js
<code>
import React, {Component} from 'react';
class Collection extends React.Component
{
render()
{
return(
<div>
<h1> {this.props.name.toString()} </h1>
</div>
);
}
}
export default Collection;
</code>
I am practicing React native. When I compile the following program, I am getting Cannot read property 'props' of undefined error for Details.js. Kindly let me know as to what went wrong here.
Layout.js
import React, {Component} from 'react';
import Header from './Header';
import Details from './Details';
export default class Layout extends React.Component {
constructor(props){
super(props);
this.state = {
heading: "Welcome no-name guy!",
header: "I am your header",
footer: "I am your footer"
};
}
render() {
return (
<div>
<Header headerprop={this.state.header} />
<Details detailprop={this.state.heading} />
</div>
);
}
}
Details.js
import React from 'react';
const Details = (detailprop) => {
return (
<div className="heading-style">{this.props.detailprop}</div>
);
};
Details.bind(this);
export default Details;
Header.js
import React, {Component} from 'react';
export default class Header extends React.Component {
render(){
return(
<div>{this.props.headerprop}</div>
);
}
}
In functional components, the props are passed as the first parameter. So, you only need to do this:
const Details = (props) => {
return (
<div className="heading-style">{props.detailprop}</div>
);
};
If you know the prop that you want to handle you can destructure that prop:
const Details = ({ detailProp }) => {
return (
<div className="heading-style">{detailprop}</div>
);
};
Your component argument should be props:
const Details = (props) => {
return (
<div className="heading-style">{props.detailprop}</div>
);
};
It could be detailprop as you have (or anything for that matter) but you would then need to access the prop by the confusing call:
detailprop.detailprop
props is the idiomatic approach for React.
Details.js is a stateless functional react component. https://facebook.github.io/react/docs/components-and-props.html
It receives props as its argument. You don't need this here.
import React from 'react';
const Details = (props) => {
return (
<div className="heading-style">{props.detailprop}</div>
);
};
Details.bind(this); // you don't need this
export default Details;
Also, div elements will not work for react-native . Please refer react native docs https://facebook.github.io/react-native/
I'm currently using Flickr api to make a Simple Image Carousel and facing a problem where my state does not get updated or rendered whenever I click the button.
Here is my index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import _ from 'lodash';
import Photo from './components/photo';
const urlArr = [];
const apiKey = "API";
const userId = "ID";
const url = `https://api.flickr.com/services/rest/?method=flickr.people.getPublicPhotos&api_key=${apiKey}&user_id=${userId}&format=json&nojsoncallback=1`;
class App extends Component {
constructor(props) {
super(props);
this.state = { urlArr: [] };
axios.get(url)
.then((photoData) => {
_.forEach(photoData.data.photos.photo, (photo) => {
urlArr.push(`https://farm6.staticflickr.com//${photo.server}//${photo.id}_${photo.secret}_z.jpg`);
});
this.setState({ urlArr });
});
}
render() {
return (
<div>
<Photo urls={this.state.urlArr}/>
</div>
);
}
};
ReactDOM.render(<App/>, document.querySelector('.container'));
and here is my photo.js
import React, { Component } from 'react';
import NextButton from './nextButton';
import PrevButton from './prevButton';
class Photo extends Component {
constructor(props) {
super(props);
this.state = { idx: 0 };
this.nextImg = this.nextImg.bind(this);
}
nextImg() {
this.setState({ idx: this.state.idx++ });
}
render() {
if (this.props.urls.length === 0) {
return <div>Image Loading...</div>
}
console.log(this.state);
return(
<div>
<PrevButton />
<img src={this.props.urls[this.state.idx]}/>
<NextButton onClick={this.nextImg}/>
</div>
);
}
}
export default Photo;
and my nextButton.js (same as prevButton.js)
import React from 'react';
const NextButton = () =>{
return (
<div>
<button>next</button>
</div>
);
};
export default NextButton;
Since I'm fairly new to React, I'm not quite sure why my this.state.idx is not getting updated when I click on the next button (Seems to me that it is not even firing nextImg function either). If anyone can give me a help or advice, that would really appreciated.
Thanks in advance!!
Update your NextButton. Use the event within your presentational component.
<NextButton next={this.nextImg}/>
And the NextButton component should looks like this.
import React from 'react';
const NextButton = (props) =>{
return (<div>
<button onClick={props.next}>next</button>
</div>
);
};
The problem lies with this piece of code:
axios.get(url)
.then((photoData) => {
_.forEach(photoData.data.photos.photo, (photo) => {
urlArr.push(`https://farm6.staticflickr.com//${photo.server}//${photo.id}_${photo.secret}_z.jpg`);
});
this.setState({ urlArr });
});
this refers to the axios.get callback scope and not the Component. You can define another variable called self or something that makes more sense to you and call self.setState().
See this question for a similar problem: Javascript "this" scope