Issue with react onClick - javascript

My app has an onClick that should be rendering more gifs. However, it does it once and then stops. Also, the onClick deletes all the gifs that were already on the page. Anyone know what I'm doing wrong?
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
};
}
componentDidMount() {
this.searchGifs('kittens');
}
searchGifs = (searchQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${searchQuery}&limit=12&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
results: response.data,
});
});
}
searchMoreGifs = (offsetQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${offsetQuery}&limit=12&offset=${this.state.results.length}&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
results: response.data,
});
});
}
render() {
return (
<main className="app">
<Header />
<SearchForm startSearch={this.searchGifs} />
<ResultList gifs={this.state.results} />
<LoadMore gifs={this.state.results} searchMore={this.searchMoreGifs} />
</main>
);
}
}
and here is the onClick:
class LoadMore extends React.Component {
render(props) {
return(
<button onClick={this.props.searchMore}>Load More</button>
);
}
}
export default LoadMore;

Each time you call this.setState({results: something}) you completely overwrite the previous state of results.You want to take the array that is in this.state.results and concat it with the new results.
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
// I also suggest moving the searchQuery to the state so it can be used in both the offset and the original search
searchQuery: 'kittens'
};
}
componentDidMount() {
this.searchGifs(this.state.searchQuery);
}
searchGifs = (searchQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${searchQuery}&limit=12&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
results: response.data,
});
});
}
searchMoreGifs = (offsetQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${offsetQuery}&limit=12&offset=${this.state.results.length}&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
// You were overwriting the old results with new data every time you ran this function
results: this.state.results.concat(response.data),
});
});
}
render() {
return (
<main className="app">
<Header />
<SearchForm startSearch={this.searchGifs} />
<ResultList gifs={this.state.results} />
{/* You also need to pass the offsetQuery to the LoadMore component so that the searchMore function can use it*/}
<LoadMore searchMore={this.searchMoreGifs} offsetQuery={this.state.searchQuery} />
</main>
);
}
}
class LoadMore extends React.Component {
render(props) {
const {offsetQuery, searchMore} = this.props
return (
<button onClick={() => searchMore(offsetQuery)}>Load More</button>
);
}
}
export default LoadMore;

Related

Pass state from one component to another in ReactJs

I am building a Weather Application, and I need to seperate the Weather card into its own component. So far, while I had the Weather card in my Weather.js file it has been working good. But now I have seperated the Weather card into its own component but I cannot access the state.
This is my main component where I have my state:
export default class Weather extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
selectedValue: ''
};
}
componentDidMount() {
fetch("http://api.weatherapi.com/v1/forecast.json?key=ca021cd2c43544e0be7112719202206&q=kosovo&days=3")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
}
render() {
const { error, isLoaded, items } = this.state;
return (
<WeatherCard items={this.state}/>
)
}
}
This is the other component that I am trying to use the state
const WeatherCard = (props) => {
return (
<div>
<h2>Today</h2>
<span>{this.props.items.location.country}</span>
</div>
)
}
The error that I get is: undefined has no properties
render() {
const { error, isLoaded, items } = this.state;
return (
<WeatherCard items={items}/>
)
}
And in your weather component
const WeatherCard = (props) => {
return (
<div>
<h2>Today</h2>
<span>{props.items.location.country}</span>
</div>
)
render() {
const { error, isLoaded, items } = this.state;
return (
<WeatherCard items={this.state}/>
)
}
change to
render() {
const { error, isLoaded, items } = this.state;
return (
<WeatherCard items={items}/>
)
}
and
const WeatherCard = (props) => {
return (
<div>
<h2>Today</h2>
<span>{this.props.items.location.country}</span>
</div>
)
}
change to this
const WeatherCard = (props) => {
return (
<div>
<h2>Today</h2>
<span>{props.items.location.country}</span>
</div>
)
}

Accessing an element inside an array with multiple objects

I'm trying to use an api with football teams, I want to simply render the name of the teams in a div or a list. But it just render the last one.
When I console.log it, it shows all the teams
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
teams: [],
isLoaded: false
}
}
componentDidMount() {
fetch('https://www.thesportsdb.com/api/v1/json/1/search_all_teams.php?l=English%20Premier%20League')
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
data: json,
})
})
}
render() {
var {
isLoaded,
data
} = this.state;
if (!isLoaded) {
return <div> Loading... < /div>
} else {
for (var key in data.teams) {
if (data.teams.hasOwnProperty(key))
console.log(data.teams[key].strTeam)
}
return ( <div className = "App" >
Premier League Teams!{
data.teams[key].strTeam
} </div>
);
}
}
}
<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>
Try this:
return (
<div className="App">
Premier League Teams!{data.teams.map(team => team.strTeam)}
</div>
);
This will loop through the teams array with map and render each one.
Try this,
import React from "react";
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
teams: [],
isLoaded: false
};
}
componentDidMount() {
fetch(
"https://www.thesportsdb.com/api/v1/json/1/search_all_teams.php?l=English%20Premier%20League"
)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
data: json
});
});
}
render() {
var { isLoaded, data } = this.state;
if (!isLoaded) {
return <div>Loading... </div>;
} else {
return (
<div className="App">
Premier League Teams!
{data.teams.map(team => (
<div> {team.strTeam} </div>
))}
</div>
);
}
}
}
export default Test;

React Expected an assignment or function call and instead saw an expression

I'm trying to render the data from my database get this instead Failed to compile.
./src/components/list-pets.component.js
Line 38:5: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.enter code here
Here is my code from the trouble component
import React, { Component } from 'react';
import axios from 'axios';
export default class ListPets extends Component {
constructor(props) {
super(props);
this.state = {
pets: []
};
}
componentDidMount = () => {
this.getPets();
};
getPets = () => {
axios.get('http://localhost:5000/pets')
.then((response) => {
const data = response.data;
this.setState({ pets: data });
console.log('Data has been received!');
})
.catch((err) => {
console.log(err);
});
}
displayPet = (pets) => {
if (!pets.length) return null;
return pets.map((pet, index) => {
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
});
};
render() {
console.log('State: ', this.state);
return (
<div className='adopt'>
{this.displayPet(this.state.pets)}
</div>
)
}
}
You need to return a value at each pets.map iteration, currently you’re returning undefined.
return pets.map((pet, index) => {
return (
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
)
});
You have to wait until fetching data is completed.
You should have to define the loading bar while fetching.
class App extends Component {
constructor() {
super();
this.state = {
pageData: {},
loading: true
}
this.getData();
}
async getData(){
const res = await fetch('/pageData.json');
const data = await res.json();
return this.setState({
pageData: data,
loading: false
});
}
componentDidMount() {
this.getData();
}
render() {
const { loading, pageData } = this.state;
if (loading){
return <LoadingBar />
}
return (
<div className="App">
<Navbar />
</div>
);
}
}

How pass data from the child component (the child has its own state) to the parent?

Expected effect: click button -> call function setEditing() -> call function item() inside setEditing() -> this.state.isEditing changes to true -> in parent this.state.isEdit changes to true. When I call the item () function, the value of isEditing does not change
App
class App extends React.Component {
constructor() {
super();
this.state = {
isEdit = false;
};
}
handleSomething = (value) => {
this.setState(prevState => {
return {
isEdit: value
};
});
}
render() {
return (
<div>
<ul>
{
this.state.todos
.map((todo, index) =>
<Todo
key={index}
index={index}
todo={todo}
handleSomething={this.handleSomething}
/>
)
}
</ul>
</div>
);
}
}
Todo
class Todo extends Component {
state = {
isEditing: false
}
setEditing = () => {
this.setState({
isEditing: !this.state.isEditing
})
this.item();
}
item = () => {
const { isEditing} = this.state;
this.props.handleSomething(isEditing);
}
render() {
return (
<button onClick={() => this.setEditing()}>Edit</button>
)
}
}
You'll need to call this.item after the state was changed, something like
setEditing = () => {
this.setState({
isEditing: !this.state.isEditing
}, this.item)
}
Also, if you want to derive a new state form the old one, you'll have to use something like this:
setEditing = () => {
this.setState(prevState => ({
isEditing: !prevState.isEditing
}), this.item)
}
Try basing your state change on the previous state, and call parent function in a callback :
setEditing = () => {
this.setState(prevState => ({
isEditing: !prevState.isEditing
}), this.item)
}
Because as written in the React doc :
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall. Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback)), either of which are guaranteed to fire after the update
has been applied. If you need to set the state based on the previous
state, read about the updater argument below.
(https://reactjs.org/docs/react-component.html#setstate)
class Todo extends React.Component {
state = {
isEditing: false
}
setEditing = () => {
this.setState({
isEditing: !this.state.isEditing
},this.item())
}
item = () => {
const { isEditing} = this.state;
this.props.handleSomething(isEditing);
}
render() {
return (
<button onClick={() => this.setEditing()}>
Edit
</button>
)
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
isEdit : false,
todos : [
"test 1",
"test 2"
]
};
}
handleSomething = (value) => {
this.setState(prevState => {
return {
isEdit: value
};
});
}
render() {
return (
<div>
<ul>
{
this.state.todos
.map((todo, index) =>
<Todo
key={index}
index={index}
todo={todo}
handleSomething={this.handleSomething}
/>
)
}
</ul>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<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>
<div id="app"></div>

How to display posted data without refresh the page?

I have a simple react app, witch can GET and POST data to an API. It's a simple gallery where pics are categorized.
At first step I get all galleries from API. That's work fine.
class Home extends Component {
constructor(props) {
super(props);
this.state = {
galleries: [],
isLoading: false,
error: null,
};
}
componentDidMount() {
this.setState({ isLoading: true });
fetch('http://.../gallery')
.then((response) => response.json())
.then((data)=>this.setState({galleries: data.galleries, isLoading: false}))
.catch(error => this.setState({ error, isLoading: false}));
}
render() {
const {galleries, isLoading, error} = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <div className="loader-wrapper"><div className="loader"/></div>;
}
return (
<div className="categories">
{ galleries.length > 0 ? galleries.map((gallery) => {
return (
<Card key={gallery.path}>
...
</Card>
)}) : null
}
<AddCategory/>
</div>
);
}
}
At next step you can create new galleries.
class AddCategory extends Component {
constructor(props) {
super(props);
this.state = {
modal: false,
galleries: [],
isLoading: false,
error: null,
};
this.toggle = this.toggle.bind(this);
this.handleClick = this.handleClick.bind(this);
}
toggle() {
this.setState({
modal: !this.state.modal
});
}
handleClick(event) {
event.preventDefault();
this.setState({
modal: !this.state.modal
});
this.setState({ isLoading: true });
fetch('http://.../gallery', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"name": this.galleryName.value})
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...')
}
})
.then((data)=>this.setState({galleries: data.galleries, isLoading: false}))
.catch(error => this.setState({ error, isLoading: false}));
}
render() {
const {modal, isLoading, error} = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <div className="loader-wrapper"><div className="loader"/></div>;
}
return (
<Card className="add">
<div className="link" onClick={this.toggle}>
<CardBody>
<CardTitle>Add gallery</CardTitle>
</CardBody>
</div>
<Modal isOpen={modal} toggle={this.toggle} className={this.props.className}>
<div className="modal-header">
...
</div>
<ModalBody>
<form className="form-inline addCategoryForm">
<div className="group">
<input type="text" ref={(ref) => {this.galleryName = ref}} id="inputGalleryName" name="galleryName" required/>
<label>name of the gallery</label>
</div>
<Button onClick={this.handleClick} color="success">Add</Button>
</form>
</ModalBody>
</Modal>
</Card>
);
}
}
The problem is that after I click on Add button nothing happened on the page, but after I refresh the page the new gallery is in the list.
Do you have any idea why I get new gallery just after refresh the page, not immediately after click on button Add?
The reason why you cannot see new galleries in the list without refreshing is that the main component, in this case the Home component, is not being re-rendered since there isn't any change in its state variables, so it does not update the page. Your usage of this.setState after getting response, from POST method using fetch, only updates and re-renders sub component AddCategory.
Add commented sections below on your components to make Home component re-render.
For Home component;
class Home extends Component {
constructor(props) {
super(props);
this.state = {
galleries: [],
isLoading: false,
error: null,
};
// Add this method binding
this.updateGalleries = this.updateGalleries.bind(this);
}
// Add this method
updateGalleries = () => {
this.setState({ isLoading: true });
fetch('http://.../gallery')
.then((response) => response.json())
.then((data)=>this.setState({galleries: data.galleries, isLoading: false}))
.catch(error => this.setState({ error, isLoading: false}));
}
componentDidMount() {
...
}
render() {
...
return (
<div className="categories">
...
/* Add updateGalleries funtion as props to AddCategory */
<AddCategory updateGalleries={this.updateGalleries}/>
</div>
);
}
}
For AddCategory component;
class AddCategory extends Component {
constructor(props) {
...
}
toggle() {
...
}
handleClick(event) {
...
// edit this field after response.json()
.then((data)=>{
this.setState({galleries: data.galleries, isLoading: false})
this.props.updateGalleries();
})
.catch(error => this.setState({ error, isLoading: false}));
}
render() {
...
}
}

Categories

Resources