How to find all elements with text react-test-renderer - reactjs

I have a component that makes a GET request and sends users their information. I need to test whether there is such an "li" with a certain text using a test. Now in my code the error is "promise returned from findByType query must be handled". How can I solve it?
describe("List Component", () => {
it("find li", () => {
const wrapper = renderer.create(<List />);
const testInstance = wrapper.root;
expect(testInstance.findByType("li")).toBeTruthy();
});
});
class List extends Component {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
getUser(this.props.user)
.then((response) => {
this.setState(response.data);
})
.catch((error) => {
this.setState({ error: "request error" });
});
}
render() {
return (
<div>
<ul>
{Object.keys(this.state).map((i) => (
<li key={i}>
{i}: {this.state[i]}
</li>
))}
</ul>
</div>
);
}
}

Related

How to call API and render a new component

What would be a way to call an API in a component class (do not use a functional component) and render the received data in a new component. I am asking for the order of operation to setState. My current solution includes setState and a function as a second argument that set anther state. Function getStockInfo calls API and then I want to render the GetQuote component switching showComponent to true. Note that in ComponentDidMount I call an initial API. Please let me know if this is a valid solution or there is a neater way.
class Portfolio extends Component {
constructor(props) {
super(props);
this.state = {
res: " ",
data: [],
showComponent: false,
};
this.showStockiaDetails = this.showStockDetails.bind(this);
this.getStockInfo = this.getStockInfo.bind(this);
this.handleClick = this.handleClick.bind(this);
}
async getStockInfo(item) {
const stock = item.symbol;
const API_KEY = "1D";
const PATH = `https://www.alphavantage.co/query?function=OVERVIEW&symbol=${stock}&apikey=${API_KEY}`;
await axios
.get(PATH)
.then((response) => {
this.setState({ res: response.data }, () => this.showStockDetails());
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
}
handleClick(item) {
this.getStockInfo(item);
}
showStockDetails() {
console.log("-->", this.state.res);
this.setState({ showComponent: true });
}
componentDidMount() {
axios
.get("http://127.0.0.1:8000/api/data")
.then((response) => {
this.setState({ data: response.data });
})
.catch(function (error) {
console.log(error);
});
}
render() {
return (
<div className="container">
<h1>Day Eight</h1>
<div className="row col-12">
<div className="col-md-5 col-lg-4 ">
<ul className="list-group">
{this.state.dayeightData.map((s) => (
<li
key={s.id}
onClick={() => this.handleClick(s)}
className="list-group-item"
aria-current="true"
>
{s.symbol}
</li>
))}
</ul>
</div>
<div className="col-md-7 col-lg-8 order-md-last">
{this.state.showComponent ? (
<GetQuote stock={this.state.res} />
) : null}
</div>
</div>
</div>
);
}
}
export default Portfolio;
You did it right. There would be many other alternatives too depending on what GetQuote component is rendering. To make it look a little bit more cleaner you can do something like this:
{
this.state.res ? <GetQuote stock={this.state.res} /> : ''
}
But make sure to initialize res as "" (without any space) as "" will be false and " " will be true.
In that way, you don't really have to call showStockDetails function.

Map doesn't work in render after fetch in React JS

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>
);
}
}

React setState fetch API

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;

reactjs - moving list items back and forth between two components

I have a small exercise I'm working on to learn ReactJS. I'm making an API call to display a list from iTunes, and when the individual list items are clicked on, they move over to a new list (kind of like a queue). When clicked on in the new list, they move back to the original list, however the list item is not being removed from the new list when it gets moved back. Here is a jsfiddle of my problem: https://jsfiddle.net/6k9ncbr6/
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
moveResults: []
}
this.handleEvent = this.handleEvent.bind(this);
}
showResults = (response) => {
this.setState({
searchResults: response.results,
moveResults: []
})
}
search = (URL) => {
$.ajax({
type: 'GET',
dataType: 'json',
url: URL,
success: function(response) {
this.showResults(response);
}.bind(this)
});
}
handleEvent = (clickedTrack) => {
const { searchResults, moveResults } = this.state;
const isInSearchResults = searchResults.some(result => result.trackId === clickedTrack.trackId);
this.setState({
searchResults: isInSearchResults ? searchResults.filter(i => i.trackId !== clickedTrack.trackId) : [...searchResults, clickedTrack],
moveResults: isInSearchResults ? [...moveResults, clickedTrack] : moveResults.filter(i => i.trackId !== clickedTrack)
});
}
componentDidMount() {
this.search('https://itunes.apple.com/search?term=broods');
}
render(){
return (
<div>
<Results
searchResults={this.state.searchResults}
handleEvent={this.handleEvent}/>
<Results
searchResults={this.state.moveResults}
handleEvent={this.handleEvent} />
</div>
);
}
}
class Results extends React.Component {
render(){
const { handleEvent, searchResults } = this.props;
return(
<ul>
{this.props.searchResults.map((result, idx) =>
<ResultItem
key={`${result.trackId}-${idx}`}
trackName={result.trackName}
track={result}
handleClick={handleEvent} />
)}
</ul>
);
}
}
class ResultItem extends React.Component {
render(){
const { handleClick, track, trackName } = this.props
return <li onClick={() => handleClick(track)}> {trackName} </li>;
}
}
ReactDOM.render(
<App />, document.getElementById('root')
);
You left off a property name:
moveResults: isInSearchResults ? [...moveResults, clickedTrack] : moveResults.filter(i => i.trackId !== clickedTrack.trackId)
The clickedTrack.trackId is missing the .trackId in your existing code.
Here's the working jsFiddle.

react getting observable values in component

Seting the obsrv array in the component below;
class AnnouncementState {
#observable categories =[];
constructor(){
this.getAnnouncementCategory();
}
getAnnouncementCategory() {
fetch(`..`)
.then((response) => {
return response.json();
})
.then((response) => {
this.categories = response.value.map((item , i)=>{ return {Id:item.Id, Title:item.Title} });
}, (error) => {
});
}
}
I checked the retrieved values its ok. and I try set it in component and render it below;
#observer
class AnnouncementComponent extends React.Component {
categories = [];
componentWillMount(){
debugger
this.categories=this.props.announcement.categories;
}
render() {
const listItems = this.categories.map((item) => {
return (<li>...</li>)
});
return (
<div id="announcements-tab">
List Items:
<ul className="nav nav-tabs">
{listItems}
</ul>
</div>
);
}
}
I expected to see all list items but none(only "listItems" string)in html, no error in console. how can I fix and debug it ? using "debugger" keyword shows nothing for observable.
In your code, I don't see where are you creating the instance of AnnouncementState. Here an example how can you get the categories list.
e.g.
class AnnouncementState {
#observable categories =[];
#action getAnnouncementCategory() {
fetch(`..`)
.then((response) => {
return response.json();
})
.then((response) => {
this.categories = response.value.map((item , i)=>{ return {Id:item.Id, Title:item.Title} });
}, (error) => {
});
}
}
export default new AnnouncementState(); //here you can create the instance.
#observer
#inject('store') //here substitute with your store name, the name you set in your provider
class AnnouncementComponent extends React.Component {
componentWillMount(){
debugger
this.props.store.getAnnouncementCategory();
}
render() {
const listItems = this.props.store.categories.map((item) => {
return (<li>...</li>)
});
return (
<div id="announcements-tab">
List Items:
<ul className="nav nav-tabs">
{listItems}
</ul>
</div>
);
}
}
This should work, just be sure you pass the correct store with <Provider store={store}>.

Resources