In this file, I'm pulling in a bunch of data from an API and assigning it to the array of "baseData". In another file, I have an onkeyup event calling the generateResults method. But then generateResults method is only getting the original, blank state of the array. I'm new to react so any help is appreciated. Thanks!
import React from 'react';
import axios from 'axios';
export class LINKS extends React.Component{
constructor(props){
super(props);
this.state={
baseData: []
}}
getBaseData(){
axios.get("http://localhost:3000/api/links")
.then(response => {
this.setState({baseData: response.data});
}).catch((error) => {
console.error(error);
});
}
componentDidMount(){
this.getBaseData();
}
generateResults(){
var linkInfo = this.state.baseData
var searchBar = document.getElementById('searchBar')
console.log(linkInfo)
for(var i = 0; i < linkInfo.length; i ++){
}
}
render(){
var linkInfo = this.state.baseData
// console.log(linkInfo)
if(linkInfo.length === 0){
return(
<div><h1> Loading... </h1></div>)
} else {
return(
<div>{linkInfo.map((info, i) =>
<div>
<u>{info['client']}</u>
{info.links.map((link, i) =>
<div> {link.linkTitle}
<br/> {link.url} </div>) }
<hr/></div>
)}</div>
)
}
}
}
Method in the other class calling the generateResults method.
handleSearch(){
let links = new LINKS
links.generateResults()
}
Because you should return any HTML into your arrow function map loop with double Parentheses like this :
<ul>
{
this.props.items.map((item) => (
<li>{item}</li>
))
}
</ul>
I've edited your code, try this :
<div>
{
linkInfo.map((info, i) => (
<div>
<u>{info['client']}</u>
{
info.links.map((link, i) => (
<div> {link.linkTitle}
<br/> {link.url} </div>
))
}
<hr/></div>
))
}
</div>
Related
I'm very new to react and trying to teach it to myself. Trying to make an API call and loop through the records. I've been successful so far, however, I can't seem to figure out how to create a new line after each element that gets displayed. I'm sure it's something simple that I'm just missing. Can someone advise here?
import React from 'react';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
}
}
async componentDidMount() {
const url = "https://api.randomuser.me/?results=10";
const response = await fetch(url);
const data = await response.json();
this.setState({ person: data, loading: false, len: data.results.length });
}
render() {
let items = [];
for (let i = 0; i < this.state.len; i++) {
const item = this.state.person.results[i].name.title;
items.push(item);
}
return (
<div>
{this.state.loading || !this.state.person ? (
<div>loading...</div>
) : (
<div>
{items}
</div>
)}
</div>
);
}
}
You can use .map() function to loop over the array display it in within the tags.
return (
<div>
{this.state.loading || !this.state.person ? (
<div>loading...</div>
) : (
<div>
{
items.map((item) => (
<>
<span>{item}</span>
<hr />
</>
)
}
</div>
)}
</div>
);
I'm struggling with fetching data and render to the screen in React JS
class Home extends Component{
componentWillMount(){
foods=[];
fetch('http://192.249.19.243:0280/main/get_recipes')
.then(res => res.json())
.then(data => foodlist=data)
.then(
() => console.log("f:",foodlist),
)
.then(
() => {foodlist.map(item => foods.push({title:item, img:"http://192.249.19.243:0280/main/image/"+item}));
console.log("foods", foods);
this.render();
}
);
}
componentDidMount(){
}
render(){
console.log("render in!");
return (
<div>
<ul>
{
console.log(foods), // this works fine -> 4 elements
foods.length!=0 ?
foods.map(item=>
<Item
title={item.title}
img={item.img}/>
)
:
<p id="loadingMsg">Data Loading...</p>
}
</ul>
</div>
);
}
}
export default Home;
in the render(), I checked console.log(foods) print 4 elements,
but Nothing appears in the screen..
I don't know why.. Please help me..
In react: it is not you who manage the render. If you want to render an element you need to call this.setState with the data that changed. You can see my example:
class Home extends Component{
state = {
foods: []
}
componentWillMount(){
fetch('http://192.249.19.243:0280/main/get_recipes')
.then(res => res.json())
.then(data => foodlist=data)
.then(
() => console.log("f:",foodlist),
)
.then(
() => {
this.setState({foods: foodlist.map(item => ({title:item, img:"http://192.249.19.243:0280/main/image/"+item})));
}
);
}
componentDidMount(){
}
render(){
console.log("render in!");
return (
<div>
<ul>
{
this.state.foods.length!=0 ?
this.state.foods.map(item=>
<Item
title={item.title}
img={item.img}/>
)
:
<p id="loadingMsg">Data Loading...</p>
}
</ul>
</div>
);
}
}
export default Home;
It looks like you are relatively new to React. I spot quite a few errors with this.
Please read the docs on class based components carefully
I have tried to refactor it without context. Give it a bash
class Home extends Component {
//initialize state in the constructor for class based components
constructor(props) {
super(props);
//foods must be an empty array otherwise .length may fail
this.state = { foods: [] }
};
//once the component has mounted, call the method which will perform the fetch
componentDidMount() {
this.fetchFoodData();
}
//calls the endpoint which returns a promise. The promise will then set the components state, which will trigger a render
fetchFoodData = () => {
fetch('http://192.249.19.243:0280/main/get_recipes')
.then(res => {
const foodData = res.json();
//not sure what your body looks like, but foods should be an array containing your food objects
const foods = foodData.map(item => foods.push({ title: item, img: "http://192.249.19.243:0280/main/image/" + item}));
//calling setState will cause react to call the render method.
this.setState({ foods: foods })
}).catch(err => {
//handle errors here
console.log(err);
});
};
//React calls this method when props or state change for this component
render() {
return (
<div>
<ul>
{
foods.length != 0 ?
foods.map(item =>
<Item
title={item.title}
img={item.img} />
)
:
<p id="loadingMsg">Data Loading...</p>
}
</ul>
</div>
);
}
}
export default Home;
Thats not the correct way to handle data in a react component. You should maintain list of foods in component state. Code sandbox: https://codesandbox.io/s/falling-bush-b9b78
As an example
import React from "react";
export default class App extends React.Component {
constructor() {
super();
this.state = {
foods: []
};
}
componentDidMount() {
const fetchMock = url =>
new Promise(resolve => {
setTimeout(() => resolve(["Barley", "Chicken", "Oats"]), 2000);
});
fetchMock("http://192.249.19.243:0280/main/get_recipes").then(foods => {
this.setState({
foods
});
});
}
render() {
console.log("render in!");
const { foods } = this.state;
return (
<div>
<ul>
{foods.length !== 0 ? (
foods.map(food => <h1 key={food}>{food}</h1>)
) : (
<p id="loadingMsg">Data Loading...</p>
)}
</ul>
</div>
);
}
}
Am learning ReactJS and building my todo application.
However am facing an issue when I try to delete a task.
I have two files TodoList.js and TodoItems.js
TodoList.js
import React, {Component} from 'react';
import TodoItems from './TodoItems';
class TodoList extends Component {
//Function to handle adding tasks
addTask(event) {
//Get task Value
let task = this.refs.name.value;
//Newitem Object
if (task !== "") {
let newItem = {
text: task,
key: Date.now()
}
this.setState({
items: this.state.items.concat(newItem)
});
this.refs.name.value = ""; //Blank out the task input box
}
}
deleteItem(key) {
var filteredItems = this.state.items.filter(function (item) {
return (item.key !== key);
});
this.setState({
items: filteredItems
});
}
constructor(props) {
super(props);
this.state = {
items: []
};
this.addTask = this.addTask.bind(this);
this.deleteItem = this.deleteItem.bind(this);
}
render() {
return (
<div className="todoListMain">
<div className="header">
<form>
<input placeholder="Enter Task" id="name" ref="name"></input>
<button type="button" onClick={this.addTask}>Add Task</button>
</form>
</div>
<div className="list">
<TodoItems entries={this.state.items} delete={this.deleteItem} />
</div>
</div>
);
}
}
export default TodoList;
TodoItems.js has following code
import React, {Component} from 'react';
class TodoItems extends Component {
constructor(props) {
super(props);
this.state = {};
}
delete(key) {
this.props.delete(key);
}
listTasks(item) {
return <li key={item.key} onClick={() => this.delete(item.key)}>{item.text}</li>
}
render() {
let entries = this.props.entries;
let listItems = entries.map(this.listTasks);
return (
<ul className="theList">
{listItems}
</ul>
);
}
}
export default TodoItems;
I am getting an error on deleting task when clicked on it.
and I am getting error as here
I guess it means function delete is not defined but it has been defined still am getting an error.
Can anyone explain how do I resolve this issue?
You should never attempt to modify your props directly, if something in your components affects how it is rendered, put it in your state :
this.state = {
entries: props.entries
};
To delete your element, just filter it out of your entries array :
delete(key) {
this.setState(prevState => ({
entries: prevState.entries.filter(item => item.key !== key)
}))
}
And now the render function :
render() {
const { entries } = this.state //Takes the entries out of your state
return (
<ul className="theList">
{entries.map(item => <li key={item.key} onClick={this.delete(item.key)}>{item.text}</li>)}
</ul>
);
}
Full code :
class TodoItems extends Component {
constructor(props) {
super(props);
this.state = {
entries: props.entries
};
}
delete = key => ev => {
this.setState(prevState => ({
entries: prevState.entries.filter(item => item.key !== key)
}))
}
render() {
const { entries } = this.state
return (
<ul className="theList">
{entries.map(item => <li key={item.key} onClick={this.delete(item.key)}>{item.text}</li>)}
</ul>
);
}
}
You should also try to never use var. If you do not plan to modify a variable, use const, otherwise, use let.
EDIT : The error shown in your edit come from listTasks not being bound to your class. To solve it you can either bind it (as shown in an other answer below) or convert it in another function :
listTasks = item => {
return <li key={item.key} onClick={() => this.delete(item.key)}>{item.text}</li>
}
Short syntax :
listTasks = ({ key, text }) => <li key={key} onClick={() => this.delete(key)}>{text}</li>
Welcome to Stackoverflow!
Check this section of the React Docs. You either have to bind your class functions in the constructor or use arrow functions.
class TodoItems extends Component {
constructor(props) {
// ...
this.delete = this.delete.bind(this);
}
delete(key) {
this.props.delete(key);
}
// Or without binding explicitly:
delete2 = (key) => {
// ...
}
}
Replace this:
onClick={this.delete(item.key)}
// passes the result of `this.delete(item.key)` as the callback
By this:
onClick={() => this.delete(item.key)}
// triggers `this.delete(item.key)` upon click
I am starting to learn React and creating my second project at the moment. I am trying to usi MovieDb API to create a movie search app. Everything is fine when I get the initial list of movies. But onClick on each of the list items I want to show the details of each movie. I have created a few apps like this using vanilla JS and traditional XHR call. This time I am using fetch API which seems straightforward ans simply to use, however when I map through response data to get id of each movie in order to retrieve details separately for each of them I get the full list of details for all the items, which is not the desired effect. I put the list of objects into an array, because after setState in map I was only getting the details for the last element. I know that I am probably doing something wrong within the API call but it might as well be my whole REACT code. I would appreciate any help.
My code
App.js
import React, { Component } from 'react';
import SearchInput from './Components/SearchInput'
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state =
{
value: '',
showComponent: false,
results: [],
images: {},
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
this.getImages = this.getImages.bind(this);
this.getData = this.getData.bind(this);
}
ComponentWillMount() {
this.getImages();
this.getData();
}
getImages(d) {
let request = 'https://api.themoviedb.org/3/configuration?api_key=70790634913a5fad270423eb23e97259'
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
images: data.images
});
});
}
getData() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.state.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
results: data.results
});
});
}
handleOnChange(e) {
this.setState({value: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
this.getImages();
this.setState({showComponent: true});
this.getData();
}
render() {
return (
<SearchInput handleSubmit={this.handleSubmit} handleOnChange={this.handleOnChange} results={this.state.results} images={this.state.images} value={this.state.value} showComponent={this.state.showComponent}/>
);
}
}
export default App;
SearchInput.js
import React, {Component} from 'react';
import MoviesList from './MoviesList';
class SearchInput extends Component {
render() {
return(
<div className='container'>
<form id='search-form' onSubmit={this.props.handleSubmit}>
<input value={this.props.value} onChange={this.props.handleOnChange} type='text' placeholder='Search movies, tv shows...' name='search-field' id='search-field' />
<button type='submit'>Search</button>
</form>
<ul>
{this.props.showComponent ?
<MoviesList value={this.props.value} results={this.props.results} images={this.props.images}/> : null
}
</ul>
</div>
)
}
}
export default SearchInput;
This is the component where I try to fetch details data
MovieList.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
let details = [];
class MoviesList extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
data.results.forEach((result, i) => {
let url = 'https://api.themoviedb.org/3/movie/'+ result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
.then((response) => {
return response.json();
}).then((data) => {
details.push(data)
this.setState({details: details});
});
});
console.log(details);
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
console.log(this.props.results)
}
render() {
let results;
let images = this.props.images;
results = this.props.results.map((result, index) => {
return(
<li ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
return (
<div>
{results}
<div>
{this.state.showComponent ? <MovieDetails details={this.state.details} results={this.props.results}/> : null}
</div>
</div>
)
}
}
export default MoviesList;
MovieDetails.js
import React, { Component } from 'react';
class MovieDetails extends Component {
render() {
let details;
details = this.props.details.map((detail,index) => {
if (this.props.results[index].id === detail.id) {
return(
<div key={detail.id}>
{this.props.results[index].id} {detail.id}
</div>
)} else {
console.log('err')
}
});
return(
<ul>
{details}
</ul>
)
}
}
export default MovieDetails;
Theres a lot going on here...
//Here you would attach an onclick listener and would fire your "get details about this specific movie function" sending through either, the id, or full result if you wish.
//Then you getDetails, would need to take an argument, (the id) which you could use to fetch one movie.
getDetails(id){
fetch(id)
displayresults, profit
}
results = this.props.results.map((result, index) => {
return(
<li onClick={() => this.getDetails(result.id) ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
Thanks for all the answers but I have actually maanged to sort it out with a bit of help from a friend. In my MovieList I returned a new Component called Movie for each component and there I make a call to API fro movie details using each of the movie details from my map function in MovieList component
Movielist
import React, { Component } from 'react';
import Movie from './Movie';
class MoviesList extends Component {
render() {
let results;
if(this.props.results) {
results = this.props.results.map((result, index) => {
return(
<Movie key={result.id} result={result} images={this.props.images}/>
)
});
}
return (
<div>
{results}
</div>
)
}
}
export default MoviesList;
Movie.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
class Movie extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
let url = 'https://api.themoviedb.org/3/movie/'+ this.props.result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
}).then((response) => {
return response.json();
}).then((data) => {
this.setState({details: data});
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
}
render() {
return(
<li ref={this.props.result.id} id={this.props.result.id} key={this.props.result.id} onClick={this.showDetails}>
{this.props.result.title}
<img src={this.props.images.base_url +`${this.props.images.poster_sizes?this.props.images.poster_sizes[0]: 'err'}` + this.props.result.backdrop_path} alt=''/>
{this.state.showComponent ? <MovieDetails details={this.state.details}/> : null}
</li>
)
}
}
export default Movie;
I have seen a lot loader plugins that work for the Mount life cycle but none for the update part and I wonder how to handle it?
What I tried was following setup for parent:
class App extends React.Component {
constructor() {
super()
this.state = {loader_wrap:false};
this.hideLoader = this.hideLoader.bind(this);
this.showLoader = this.showLoader.bind(this);
}
hideLoader(){
this.setState({loader_wrap: false});
}
showLoader() {
this.setState({loader_wrap: true});
}
render() {
var loaderStyle;
if (this.state.loader_wrap) {
loaderStyle = {display:"block"};
} else {
loaderStyle = {display:"none"};
}
return (
<div>
<div id="content">
{React.cloneElement(content, {
hideLoader: this.hideLoader,
showLoader: this.showLoader
})}
</div>
<div id="loader-wrap" style={loaderStyle}>
<img className="loader hidden-sm hidden-xs" src='source/file/'>
</div>
</div>
)
}
}
And this is the child calling the methods:
class Childextends React.Component {
constructor() {
super();
this.state = {results:[]};
this.calculate = this.calculate.bind(this);
}
calculate(dict) {
this.props.showLoader();
Actions.action(dict)
.then(results => {
this.setState({results: results});
})
.catch((err) => {
var errResp = JSON.parse(err.response);
console.log(errResp);
this.setState({responseErrors: errResp});
});
}
componentDidMount() {
this.props.hideLoader();
}
componentDidUpdate() {
this.props.hideLoader();
}
componentWillReceiveProps(values){
this.setState({results:values.results});
}
render() {
return (
/*stuff to be returned*/
)
}
}
I also tried to use the Will methods .. which worked even worser :D
Any ideas how to implement this? I use react with flux but don't now how to use it in this case ..
Why not just call hideLoader() in the callback of the action's promise?
class Childextends React.Component {
constructor() {
super();
this.state = {results:[]};
this.calculate = this.calculate.bind(this);
}
calculate(dict) {
this.props.showLoader();
Actions.action(dict)
.then(results => {
this.setState({results: results});
})
.catch((err) => {
var errResp = JSON.parse(err.response);
console.log(errResp);
this.setState({responseErrors: errResp});
})
.then(() => {
this.props.hideLoader();
});
}
render() {
return (
/*stuff to be returned*/
)
}
}
Edit: A different approach to the parent component as well - rather than hiding the element with a style, just don't render it if it isn't required.
render() {
return (
<div>
<div id="content">
{React.cloneElement(content, {
hideLoader: this.hideLoader,
showLoader: this.showLoader
})}
</div>
{this.state.loader_wrap &&
<div id="loader-wrap" style={loaderStyle}>
<img className="loader hidden-sm hidden-xs" src='source/file/'>
</div>
}
</div>
)
}