Pass props to different components in react - reactjs

Just having troubles, sorry for the noob question, but i can't seem to log the props results in the DisplayData.js file. I am trying to pass the SearchStockResult state to the DisplatData.js file... I have tried to console log the data property with no luck. Not to sure what is going on. Excuse the naming conventions as I had just changed it from a stock search to a crypto search.
import React, { Component } from 'react'
import DisplayData from './DisplayData';
export default class stockSearch extends Component {
state = {
searchResult: '',
}
componentDidMount = () => {
fetch(`https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,IOT&tsyms=USD`)
.then((response) => response.json())
.then(data => {
this.setState({ searchResult: data });
console.log(this.state.searchResult);
});
}
render() {
const {data} = this.state;
return (
<form>
<label>
Name:
<input type="text" name="query" />
</label>
<button>Search Crypto</button>
<DisplayData results={data} />
</form>
)
}
}
import React, { Component } from 'react'
export default class DisplayData extends Component {
dataResults = (props) => {
console.log('from data results', props.results);
}
render() {
return (
<div>
</div>
)
}
}

Make few corrections:
State update is async. So pass a call back function if you want to print it
in StockSearch component, you need to destructure searchResult from state (not data)
In DisplayData component use this.props. Also call your function(dataResults) in render method so that it is called and rendered
Working copy of your code is here
StockSearch Component
import React, { Component } from "react";
import DisplayData from "./DisplayData";
export default class StockSearch extends Component {
state = {
searchResult: ""
};
componentDidMount = () => {
fetch(
`https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,IOT&tsyms=USD`
)
.then(response => response.json())
.then(data => {
this.setState({ searchResult: data }, () => {
console.log("api result", this.state.searchResult);
});
});
};
render() {
const { searchResult } = this.state;
return (
<form>
<label>
Name:
<input type="text" name="query" />
</label>
<button>Search Crypto</button>
<DisplayData results={searchResult} />
</form>
);
}
}
DisplayData Component
import React, { Component } from "react";
export default class DisplayData extends Component {
dataResults = () => {
console.log("from data results", this.props.results);
};
render() {
this.dataResults() // just to print
return <div>{return <div>{JSON.stringify(this.props.results)}</div>;}</div>;
}
}

Related

React testing with Jest and Enzyme: How to write test for connected components with API call and props

I am fairly new to testing React applications with Jest and Enzyme. I am trying to test some connected components, but don't understand how to mock data for those. I have an API call and use props. Here are my files:
News Page
import React, { Component } from 'react';
import './news.style.css';
import NewsList from '../../components/news-list/news-list.component';
import { SearchBar } from '../../components/search-bar/search-bar.component';
import Header from '../../components/header/header.component';
import { NewsFooter } from '../../components/news-footer/news-footer.component';
class News extends Component {
constructor() {
super();
this.state = {
news: [],
searchField: '',
topics: ''
};
}
componentDidMount() {
fetch('https://sheltered-earth-94987.herokuapp.com/news')
.then(response => response.json())
.then(news => this.setState({ news: news}));
}
render() {
const { news, searchField, topics } = this.state;
const filteredNewsname = news
.filter(news => news.news_topic.toLowerCase().includes(topics.toLowerCase()))
.filter(news => news.news_headline.toLowerCase().includes(searchField.toLowerCase()));
return (
<div>
<Header/>
<h1 className="pageheadline">News</h1>
<SearchBar
placeholder='Search News'
handleChange= {e => this.setState({ searchField: e.target.value})}
/>
<div className="newslist">
<NewsList news={filteredNewsname}>
</NewsList>
</div>
<div className="newsfooter">
<NewsFooter
handleClick= {e => this.setState({ topics: e.target.id})}
/>
</div>
</div>
);
}
}
export default News;
NewsList Component
import React from 'react';
import './news-list.style.css';
import { NewsCard } from '../news-card/news-card.component';
import { Link } from 'react-router-dom';
const NewsList = props => {
return <div className='news-list'>
{
props.news.map(newsentry => <Link to={`/news/${newsentry.news_id}`}>
<NewsCard key={newsentry.news_id} newsentry={newsentry}/></Link>)
}
</div>;
};
export default NewsList;
NewsCard Component
import React from 'react';
import './news-card.style.css';
const NewsCard = props => (
<div className='news-card-container' data-test="news-card-container">
<img className="newsimg" alt="Newsimage" src={ props.newsentry.news_header_image}></img>
<div className="newsinfo">
<h4 className="newstitle"> { props.newsentry.news_headline } </h4>
<p className="teaser">{props.newsentry.news_teaser}</p>
<p className="author">By {props.newsentry.news_author} </p>
</div>
<p className="newstopic">#{props.newsentry.news_topic}</p>
</div>
)
export default NewsCard;
How can I test the NewsList and the NewsCard Components with mocked data?
This is how I started:
Testfile
import { shallow } from 'enzyme';
import React from 'react';
import NewsCard from './news-card.component';
import { findByTestAttr } from '../../../utils/index';
const setUp = (props={}) => {
const component = shallow(<NewsCard {... props}/>);
return component;
}
describe('NewsCard Component', () => {
describe('Have props', () => {
let wrapper;
beforeEach(() => {
const props = {
news: [],
};
wrapper = setUp(props);
});
it('Should render without errors', async () => {
const component = findByTestAttr(wrapper, 'news-card-container');
expect(component.length).toBe(1);
})
});
})
File with findByTestAttr function
export const findByTestAttr = (component, attr) => {
const wrapper = component.find(`[data-test='${attr}']`);
return wrapper;
}
For this right now I get an error, saying:
TypeError: Cannot read properties of undefined (reading 'news_header_image')
Before rendering components in test block; you can provide a new, mockup variable to your component.
Example:
<NewsCard key={..} newsentry={mockupNewsentry}/> with mockupNewsentry being your mockup data variable.
Longer example:
test("renders singleitem with data", async () => {
const mockupData = {
name: "Homer Simpson",
job: "Nuclear Safety Inspector",
id: "14",
};
render(
<SingleItem data={mockupData} />
);
const element = await screen.findByText(/Homer Simpson/i);
expect(element).toBeInTheDocument();
});
Check out this package. It will mock the network layer. Everyone is using this one for integration testing of components.
https://mswjs.io

Passing a state value in functional component in React

I am trying to use promise tracker. I used react-promise-tracker package.
Below the code of my component:
import React from "react";
import { usePromiseTracker } from "react-promise-tracker";
import Spinner from "./Spinner";
const LdngIndicator = (props) => {
const { promiseInProgress } = usePromiseTracker();
return promiseInProgress && <Spinner />;
};
export default function LoadingIndicator() {
return <LdngIndicator />;
}
parent.js
import React, { Component } from "react";
import { trackPromise } from "react-promise-tracker";
import LoadingIndicator from "./LoadingIndicator";
export default class App extends Component {
constructor() {
super();
this.state = {
randomResponse: "",
};
}
apifunc = () => {
trackPromise(
fetch(
"https://****.rapidapi.com/random",
{
method: "GET",
headers: {
accept: "application/json",
"x-rapidapi-key": process.env.REACT_APP_KEY,
"x-rapidapi-host": "host.rapidapi.com",
},
}
)
.then((res) => res.json())
.then((res) => {
this.setState({
randomResponse: res,
});
})
.catch((err) => {
console.error(err);
})
);
};
handleSubmit = (e) => {
e.preventDefault();
this.apifunc();
};
render() {
return (
<div className="wrapper">
<form className="jokesForm" onSubmit={this.handleSubmit}>
<label>Get a new joke</label>
<input type="submit" value="Go!"></input>
</form>
<main>
{<LoadingIndicator /> ? <p>True</p> : <p>false</p>}
</main>
</div>
);
}
}
I can run this without any issues. I would like to get boolean value of the promiseInProgress in my parent component.
promiseInProgress changes as I make requests.
What is the best way to send this as props?
I read https://reactjs.org/docs/components-and-props.html as well as different questions on stack overflow but I was not able to find an answer to my quesiton.
I appreciate any help.
you didn't show us your parent component though so let's assume its a functional component
//parent.jsx
import react, { useState } from 'react'
import Loader from 'loader.jsx'
const ParentComp = () => {
const [state, setState] = useState(false)
return <>
<Loader setState={setState} setState={setState} />
<span> {`state: ${state}`} <span/>
</>
}
export default ParentComp
//loader.jsx
import React from "react";
import { usePromiseTracker } from "react-promise-tracker";
import Spinner from "./Spinner";
const LdngIndicator = () => {
const { promiseInProgress } = usePromiseTracker();
return promiseInProgress && <Spinner />;
};
export default function LoadingIndicator({state, setState}) {
return <>
<button onClick={() => setState(!state)}>click</button>
<LdngIndicator />
</>
}
you get the idea? what you can easily do is to pass setState to child component and modify it from the child

Target not defined react

sorry for the noob question, but I am getting a target undefined. I've tried passing the componentDidMount on my onformsubmit however React is telling me the query variable is not defined.
import React, { Component } from 'react'
import DisplayData from './DisplayData';
export default class stockSearch extends Component {
state = {
searchResult: {},
}
componentDidMount = (e) => {
const query = e.target.elements.query.value
fetch(`https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,IOT&tsyms=USD`)
.then((response) => response.json())
.then(data => {
this.setState({ searchResult: data });
console.log(this.state.searchResult);
});
}
render() {
const { searchResult } = this.state;
return (
<form onSubmit={this.props.componentDidMount}>
<label>
Name:
<input type="text" name="query" placeholder="Search Crypto" />
</label>
<button>Search Crypto</button>
<DisplayData results={searchResult} />
</form>
);
}
}
componentDidMount is one of the React Component lifecycle methods so you shouldn't pass it as the onSubmit handler. Instead, you should create a new method, e.g fetchData, which you pass to the form's onSubmit.
If you want to also fetch data on mount, you can call your handler in componentDidMount
export default class StockSearch extends Component {
state = {
searchResult: {},
queryValue: ''
}
componentDidMount() {
fetchData('default');
}
fetchData = (query) => {
fetch(`http://something.com/${query}`)
.then(...)
.then(data => {
this.setState({ searchResult: data })
});
}
render() {
return (
<form onSubmit={() => fetchData(this.state.queryValue)}>
<input
value={this.state.queryValue}
onChange={(e) => this.setState(e.target.value)}
/>
</form>
)
}
}
A few other things I've changed:
1. React components should be UpperCamelCase
2. Generally you'll manage state in your component, for example input values
.

fire onClick function from child component

TLDR: How would i retrieve the console.log from the child component
after url has been submitted
I want to be able to get the response data from the child component after a url has been submitted. How would i be able to do this ?
In other words how i be able to fire the onClick function after a url has been submitted ?
code sandbox
https://codesandbox.io/s/cool-feather-fjkv6
Parent
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import Child from './Child';
class Parent extends Component {
constructor(props){
super(props);
this.state = {
url:'',
outputUrl:''
}
}
onChange = (e) => {
e.preventDefault();
this.setState({
url: e.target.value
})
}
onSubmit = (e) => {
e.preventDefault();
console.log(this.state.url)
}
render(){
return (
<div className="App">
<form onSubmit={this.onSubmit}>
<input type="text" onChange={this.onChange} value={this.state.url} placeholder="Enter url " />
<button type="submit" >Submit</button>
</form>
{/* if have url else enter test url */}
<Child url={this.state.url} method={'GET'} />
</div>
);
}
}
export default App;
How would i retrieve the console.log from the child component after url has been submitted ? I'm slightly confused. Sorry if this is looks confusing.
Child
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
class Child extends Component {
constructor(props){
super(props);
this.state = {
data:[]
}
}
// componentDidUpdate or try this
onClick(){
const url = `${this.props.url}`;
if(this.props.method === "GET"){
axios.get(url).then( res => {
this.setState({
data: res.data
})
console.log(this.state.data)
})
}
else if(this.props.method === "POST"){
axios.get(url).then( res => {
this.setState({
data: res.data
})
console.log(this.state.data)
})
}
}
render(){
return (
null
);
}
}
export default Child;
Please! see render method on Child Component
class Child extends Component {
static onClick() {
// do something
}
constructor(props) {
super(props);
this.state = {
data: []
};
}
// componentDidUpdate or try this
onClick = e => {
e.preventDefault();
const url = `${this.props.url}`;
if (this.props.method === "GET") {
axios.get(url).then(res => {
this.setState({
data: res.data
});
console.log(this.state.data);
});
} else if (this.props.method === "POST") {
axios.get(url).then(res => {
this.setState({
data: res.data
});
console.log(this.state.data);
});
}
};
render() {
return <div onClick={this.onClick}>Click Me</div>;// <------
}
}

request data, pass it to another component and return it in react

I want to display search results on a page. My idea was to make a submit that sends a request to the server. After receiving the data I iterate through it and call a function to process it in another component which should render processed data back to the parent component. Unfortunately no data rendered in the search component and no errors were given to debug.
The main problem right now is that no data is returned from SearchResults; also I'm not sure if the data is received in that component.
import React, { Component } from "react";
import axios from "axios";
import SearchResults from "./SearchResults";
export default class SearchComponent extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
query: "",
searchresults: []
};
}
submitSearchResults(searchresults) {
return this.state.searchresults.map(function(object, i) {
return <SearchResults obj={object} key={i} />;
});
}
onSubmit(e) {
e.preventDefault();
axios
.get("http://localhost:4200/serverport/spotify/" + this.state.song)
.then(res => {
const searchresults = res.data;
for (var key in searchresults) {
if (searchresults.hasOwnProperty(key)) {
for (var i = 0; i < searchresults[key].items.length; i++) {
this.submitSearchResults(searchresults[key].items[i]);
}
}
}
});
}
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
value={this.state.query}
onChange={this.onChangeSong}
/>
<input value="search" type="submit" />
</form>
<div>{this.submitSearchResults()}</div>
</div>
);
}
}
This is the code that requests the data from server. I removed some irrelevant code to make it more readable.
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
class SearchResults extends Component {
constructor(props) {
super(props);
}
render() {
return <div>works</div>;
}
}
export default SearchResults;
This is the file where the data is supposed to be processed. I only render "works" for debugging.
Instead of calling the function that renders your search result JSX when the request is complete, you can set the searchresults in the state and the component will re-render automatically.
Example
const axios = {
get: () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ data: [{ text: "foo" }, { text: "bar" }] });
}, 1000);
});
}
};
class SearchComponent extends React.Component {
state = {
song: "",
searchresults: []
};
submitSearchResults(searchresults) {
return this.state.searchresults.map(function(object, i) {
return <SearchResults obj={object} key={i} />;
});
}
onSubmit = e => {
e.preventDefault();
axios
.get("http://localhost:4200/serverport/spotify/" + this.state.song)
.then(res => {
const searchresults = res.data;
this.setState({ searchresults });
});
};
onChangeSong = e => {
this.setState({ song: e.target.value });
};
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
value={this.state.song}
onChange={this.onChangeSong}
/>
<input value="search" type="submit" />
</form>
<div>{this.submitSearchResults()}</div>
</div>
);
}
}
class SearchResults extends React.Component {
render() {
return <div>{this.props.obj.text}</div>;
}
}
ReactDOM.render(<SearchComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Resources