I have a components, which shows 3 first images from json array.
I want to add a button which will replace those first 3 items with next 3 in the same array.
I guess I'm missing something here
import React, {Component} from 'react';
import '../App.css';
import ImageList from "./ImageList";
class App extends Component {
constructor() {
super();
this.state = {
images: [],
index: 0
};
this.toNext = this.toNext.bind(this);
}
componentDidMount() {
fetch("https://picsum.photos/v2/list")
.then(res => res.json())
.then(data => {
this.setState({ images: data });
})
.catch(err => {
console.log('Error happened during fetching!', err);
});
}
toNext = () => {
this.setState({index: (this.state.index + 3)});
}
render() {
return (
<div className="container">
<h2 className="title">Images list</h2>
<ImageList data={this.state.images}/>
<button onClick={this.toNext} className="next-btn">Next</button>
</div>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.0/umd/react-dom.production.min.js"></script>
The index is being updated correctly, but you are not taking that updated value into account. This could be achieved by slicing the images collection in the render method:
render() {
const minIndex = this.state.index;
const maxIndex = minIndex + 3;
return (
<div className="container">
<h2 className="title">Images list</h2>
<ImageList data={this.state.images.slice(minIndex, maxIndex)}/>
<button onClick={this.toNext} className="next-btn">Next</button>
</div>
)
}
Related
After two days of being stuck on this component, I'm asking for any sort of help. I'm trying to search an API based on user input, and then filter that down to a more specific option as the user keeps typing. After solving a dozen or so errors, I'm still left with "Can't find variable 'Query'", and I just can't seem to find or figure out what exactly it's wanting. There was another post on here that led me in the right direction, but didn't provide any sort of answer for the issue I'm having. Any help here would be appreciated.
import axios from "axios";
import axiosRateLimit from "axios-rate-limit";
import React, { Component } from "react";
import SearchBar from "react-native-elements/dist/searchbar/SearchBar-ios";
class CardSearch extends Component {
state = {
data: [],
filteredData: [],
query: "",
};
handleInputChange = (event) => {
const query = event.target.value;
this.setState((prevState) => {
const filteredData = prevState.data.filter((element) => {
return element.name.toLowerCase().includes(query.toLowerCase());
});
return {
query,
filteredData,
};
});
};
getData = () => {
axiosRateLimit(
axios.get(`https://api.scryfall.com/cards/autocomplete?q=${query}`),
{ maxRPS: 8 }
)
.then((response) => response.json())
.then((data) => {
const { query } = this.state;
const filteredData = data.filter((element) => {
return element.name.toLowerCase().includes(query.toLowerCase());
});
this.setState({
data,
filteredData,
});
});
};
componentWillMount() {
this.getData();
}
render() {
return (
<>
<SearchBar
placeholder='Search For...'
value={this.state.query}
onChange={this.handleInputChange}
/>
<div>
{this.state.filteredData.map((i) => (
<p>{i.name}</p>
))}
</div>
</>
);
}
}
export default CardSearch;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Have a look at this Link. You are not setting the State in a Constructor. And as already mentioned in the comments you will then have to access the query using this.state.query
https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
The Code-Sample from the React Documentation:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
EDIT - I fixed this and posted the working code.
I'm working on a project and I am having a specific issue I can't figure out how to fix. I am displaying a list of champions images and when the user clicks on one of them (s) then it will change the page to display that champions name. Currently I can console.log any of the names without any issues which means my functional component Newchamp() is working! However I am having trouble passing an argument from NewChamp to the class component SpecificChamp. When I add the last line in Newchamp return and try to display it in SpecificChamp using {s} its undefined!
Is it possible to pass an argument from my functional class to my component class? if not how can I get the page to change to the specific image that is clicked? I am new to react and appreciate any help!
Can anyone please help me out with this
import React, { Component } from 'react';
import './Champions.css';
class AllChamps extends Component {
render() {
let champion = this.props.champion;
return(
<div className='champions'>
<h1> all champions</h1>
{Object.keys(this.props.champions).map((s) => (
<div className='champs' onClick={() => this.props.NewChamp({s, champion})}>
<img
alt='Champion Images'
src={`http://ddragon.leagueoflegends.com/cdn/10.16.1/img/champion/${s}.png`}
onClick={this.props.onClick}
></img>
{s}
</div>
))}
</div>
)}}
class SpecificChamp extends Component {
render() {
let champion = this.props.champion
let Spec = champion[champion.length - 1];
return (
<div className='champions'>
<h1> 1 champions</h1>
<div className='champs'>
<button onClick={this.props.onClick}></button>
{Spec}
</div>
</div>
)}
}
class Champions extends Component {
constructor(props) {
super(props);
this.handleAllChamps = this.handleAllChamps.bind(this);
this.handleSpecificChamp = this.handleSpecificChamp.bind(this);
this.NewChamp = this.NewChamp.bind(this);
this.state = {
champions: [],
champion: [],
clickedChamp: false,
thisChamp: 'ahri'
}}
NewChamp = (props) =>
{
let s = props.s;
props.champion.push(s);
fetch(`http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion/${s}.json`)
.then(response => { return response.json() })
.then((response) => {
Object.keys(response.data).map((a) => (s = a
))})
fetch(`http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion/${s}.json`)
.then(response => { return response.json() })
.then((response) => {
console.log(s)
console.log(response.data)
console.log(props.champion)
})
console.log(`http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion/${s}.json`);
}
handleAllChamps = (props) => {
this.setState({ clickedChamp: true,
})};
handleSpecificChamp = () => {
this.setState({ clickedChamp: false,
})};
componentDidMount(props) {
const apiUrl = `http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion.json`;
fetch(apiUrl)
.then(response => { return response.json() })
.then((response) => {
this.setState({
champions: response.data
}, () => (this.state.champions))
return
})
}
render() {
const clickedChamp = this.state.clickedChamp;
let display;
if (clickedChamp ) {
display = <SpecificChamp champion={this.state.champion} onClick={this.handleSpecificChamp} s={this.state.thisChamp}/>;
} else {
display = <AllChamps champions={this.state.champions} onClick={this.handleAllChamps} NewChamp={this.NewChamp} thisChamp={this.state.thisChamp} champion={this.state.champion} />;
}
return (
<div>
<div className='champions'></div>
{display}
</div>
);
}
}
export default Champions;
The render function in class component does not has any props. You should use props from this like what you have done with handle click.
class SpecificChamp extends Component {
render() {
return (
<div className='champions'>
<h1> 1 champions</h1>
<div className='champs'>
<button onClick={this.props.onClick}></button>
{this.props.s}
</div>
</div>
)}
}
So the goal is to fetch data from the google books API, which returns JSON data in the same form as my state shows. I want to update title with the title string returned by the JSON data. Right now I get a "failed to compile" on the line I've marked in the code. Then, I would like to pass the title as a props to the List component, which would render it as a list item with each map through. So if 20 books' data are fetched, I would render 20 different titles. I'm new to react so I'm not sure how much is wrong here.
import React, { Component } from 'react';
import List from './List.js';
export default class Main extends Component {
state ={
items : [{
volumeInfo : {
title : "",
}
}]
}
componentDidMount() {
fetch(`https://www.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=AIzaSyAWQ0wFzFPQ3YHD_uLDC7sSs-HPRM3d__E`)
.then(res => res.json())
.then(
(result) => {
this.setState({
items : [{
volumeInfo : {
title : result.items.map((book) => {
const name = book.volumeInfo.title;
return name;
})
}
}] });
})
}
render() {
return (
<div>
<header>
<h2>Google Book Search</h2>
</header>
<List title={this.state.items}/>
</div>
)
}
}
Here's List.js
import React, { Component } from 'react'
export default class List extends Component {
render() {
return (
<div>
<ul>
<li>{this.props.items}</li>
</ul>
</div>
)
}
}
As the result of your fetch() has the same structure as your items property of the state, all you need to do in the then() callback is to set the result in the state directly as shown below:
componentDidMount() {
fetch('your/long/url')
.then(res => res.json())
.then((result) => {
this.setState({ items: (result.items || []) });
});
}
Now that your state is updated with the needed data, you need to pass it as a prop to your List component:
render() {
return (
<div>
<header>
<h2>Google Book Search</h2>
</header>
<List items={ this.state.items } />
</div>
);
}
Finally, in your List component, you can make use of this prop by rendering it in a map() call:
render() {
return (
<div>
<ul>
{ this.props.items.map((book, i) => (
<li key={ i }>{ book.volumeInfo.title }</li>
)) }
</ul>
</div>
);
}
export default class Main extends Component {
state ={
items : []
}
componentDidMount() {
fetch(`https://www.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=AIzaSyAWQ0wFzFPQ3YHD_uLDC7sSs-HPRM3d__E`)
.then(res => res.json())
.then((result) => {
const titleList = result.items.map((item)=>{return item.volumeInfo.title});
this.setState({items: titleList})
})
};
render(){
const {items} = this.state;
const titleComponent = items.length > 0
? items.map((item)=>{
return <List title={item} />
})
: null;
return (
<div className="App">
<header>
<h2>Google Book Search</h2>
</header>
{titleComponent}
</div>
)
}
}
Above code should be worked if your List component is working fine.
Change the setState function with this
this.setState({
items : [{
volumeInfo : {
title : result.items.map((book) => {
const name = book.volumeInfo.title;
return name;
})
}
}] });
Looks like brackets was the issue.
I'm trying to get some map over some data and render it as a card style component.
The data structure is as follows within my user array (all fake data)
and this is the code...
import React from "react";
import OverlayContent from "./OverlayContent";
import { onCountryClick } from "../Scene3D/AppSignals";
import Portal from "./Portal";
import "./style.scss";
class Overlay extends React.Component {
constructor(props) {
super(props);
this.state = { overlay: false, users: [], country: [] };
this.openOverlay = this.openOverlay.bind(this);
this.closeOverlay = this.closeOverlay.bind(this);
}
openOverlay() {
this.setState({ overlay: true });
}
closeOverlay() {
this.setState({ overlay: false });
}
onCountryClick = (country, users) => {
this.openOverlay();
this.setState({ users: [users], country });
};
componentDidMount() {
this.onCountrySignal = onCountryClick.add(this.onCountryClick);
}
componentWillUnmount() {
this.onCountrySignal.detach();
}
render() {
const country = this.state.country;
const users = this.state.users;
console.log(users);
return (
<div className="btn-container">
{this.state.overlay && (
<Portal>
<div>
<h1>{country}</h1>
{users &&
users.map(user => (
<div className="user_container">
<h1 key={user.id} className="user_name">
{user.favouriteQuote}
</h1>
</div>
))}
<button className="btn" onClick={this.closeOverlay}>
Close
</button>
</div>
</Portal>
)}
</div>
);
}
}
export default Overlay;
When mapping on this user array it doesn't appear to give me access to the actual user data I need. Is it because I have an array within an array?
onCountryClick = (country, users) => {
this.openOverlay();
this.setState({ users: [users], country });
};
Is users an array? If so remove the brackets here or map through the first element. users[0].map()
I am facing such problem, i got my array of records fetched from an API, mapped it into single elements and outputting them as single components. I have function which changes state of parent Component, passes value to child component and child component should hide/show div content after button is clicked.
Of course. It is working, but partially - my all divs are being hidden/shown. I have set specific key to each child component but it doesn't work.
App.js
import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
import countries from '../../countriesList';
import CitySearchForm from './CitySearchForm/CitySearchForm';
import CityOutput from './CityOutput/CityOutput';
import ErrorMessage from './ErrorMessage/ErrorMessage';
class App extends Component {
state = {
country: '',
error: false,
cities: [],
infoMessage: '',
visible: false
}
getCities = (e) => {
e.preventDefault();
const countryName = e.target.elements.country.value.charAt(0).toUpperCase() + e.target.elements.country.value.slice(1);
const countryUrl = 'https://api.openaq.org/v1/countries';
const wikiUrl ='https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&format=json&category=city&redirects&origin=*&titles=';
const allowedCountries = new RegExp(/spain|germany|poland|france/, 'i');
if (allowedCountries.test(countryName)) {
axios
.get(countryUrl)
.then( response => {
const country = response.data.results.find(el => el.name === countryName);
return axios.get(`https://api.openaq.org/v1/cities?country=${country.code}&order_by=count&sort=desc&limit=10`)
})
.then( response => {
const cities = response.data.results.map(record => {
return { name: record.city };
});
cities.forEach(city => {
axios
.get(wikiUrl + city.name)
.then( response => {
let id;
for (let key in response.data.query.pages) {
id = key;
}
const description = response.data.query.pages[id].extract;
this.setState(prevState => ({
cities: [...prevState.cities, {city: `${city.name}`, description}],
infoMessage: prevState.infoMessage = ''
}))
})
})
})
.catch(error => {
console.log('oopsie, something went wrong', error)
})
} else {
this.setState(prevState => ({
infoMessage: prevState.infoMessage = 'This is demo version of our application and is working only for Spain, Poland, Germany and France',
cities: [...prevState.cities = []]
}))
}
}
descriptionTogglerHandler = () => {
this.setState((prevState) => {
return { visible: !prevState.visible};
});
};
render () {
return (
<div className="App">
<ErrorMessage error={this.state.infoMessage}/>
<div className="form-wrapper">
<CitySearchForm getCities={this.getCities} getInformation={this.getInformation} countries={countries}/>
</div>
{this.state.cities.map(({ city, description }) => (
<CityOutput
key={city}
city={city}
description={description}
show={this.state.visible}
descriptionToggler={this.descriptionTogglerHandler} />
))}
</div>
);
}
}
export default App;
CityOutput.js
import React, { Component } from 'react';
import './CityOutput.css';
class CityOutput extends Component {
render() {
const { city, descriptionToggler, description, show } = this.props;
let descriptionClasses = 'output-record description'
if (show) {
descriptionClasses = 'output-record description open';
}
return (
<div className="output">
<div className="output-record"><b>City:</b> {city}</div>
<button onClick={descriptionToggler}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
)
}
};
export default CityOutput;
Put the visible key and the toggle function in the CityOutput instead of having it in the parent
import React, { Component } from "react";
import "./CityOutput.css";
class CityOutput extends Component {
state = {
visible: true
};
descriptionTogglerHandler = () => {
this.setState({ visible: !this.state.visible });
};
render() {
const { city, description } = this.props;
let descriptionClasses = "output-record description";
if (this.state.visible) {
descriptionClasses = "output-record description open";
}
return (
<div className="output">
<div className="output-record">
<b>City:</b> {city}
</div>
<button onClick={() => this.descriptionTogglerHandler()}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
);
}
}
export default CityOutput;
There are two ways of how I would approach this,
The first one is setting in your state a key property and check and compare that key with the child keys like:
state = {
country: '',
error: false,
cities: [],
infoMessage: '',
visible: false.
currKey: 0
}
descriptionTogglerHandler = (key) => {
this.setState((prevState) => {
return { currKey: key, visible: !prevState.visible};
});
};
// then in your child component
class CityOutput extends Component {
render() {
const { city, descriptionToggler, description, show, currKey, elKey } = this.props;
let descriptionClasses = 'output-record description'
if (show && elKey === currKey) {
descriptionClasses = 'output-record description open';
}
return (
<div className="output">
<div className="output-record"><b>City:</b> {city}</div>
<button onClick={() => descriptionToggler(elKey)}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
)
}
};
The other way is to set an isolated state for every child component
class CityOutput extends Component {
constructor(props) {
this.state = {
show: false
}
}
function descriptionToggler() {
const {show} = this.state;
this.setState({
show: !show
})
}
render() {
const { city, descriptionToggler, description } = this.props;
let descriptionClasses = 'output-record description'
if (this.state.show) {
descriptionClasses = 'output-record description open';
}
return (
<div className="output">
<div className="output-record"><b>City:</b> {city}</div>
<button onClick={descriptionToggler}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
)
}
};
I hope this helps ;)