I'm having a problem with the function fetch. I'm trying to send just a number for example "1", and I have access to this data in all child components, but after calling fetch, I'm no longer able to access this data.
App.jsx:
import React, { Component } from 'react'
import './App.css';
import fetch from 'isomorphic-fetch'
import Header from './Header'
import Content from './Content'
import Footer from './Footer'
class App extends Component {
constructor(props) {
super(props)
this.state = {
stripdata: null
}
}
componentWillMount() {
fetch(`http://localhost:3000/data/info.json`)
.then(results => results.json())
.then(data => {
this.setState({
stripdata: data
})
// console.log(this.state.stripdata)
})
.catch(err => {
console.log("Didn't connect to API", err)
})
}
render() {
// console.log(this.state.stripdata)
return (
<div className="App">
<Header onQuery={1}/>
{
(this.state.data === null) ? <div className="loading">Loading data...</div> : <Content onResult={this.state.stripdata}/>
}
<Footer />
</div>
);
}
}
export default App;
Content.jsx:
import React, { Component } from 'react'
import Result from './Result'
class Content extends Component {
constructor(props) {
super(props);
this.state = {
stripdata: this.props.onResult
};
}
componentWillMount() {
}
render() {
console.log("im an Content: " + this.state.stripdata)
return (
<div className="Content">
<Result stripdata={ this.state.stripdata }/>
</div>
);
}
}
export default Content;
Result.jsx:
import React, { Component } from 'react'
import PersonCard from './PersonCard'
class Result extends Component {
constructor(props) {
super(props);
this.state = {
stripdata: this.props.stripdata
};
}
componentWillMount() {
}
render() {
console.log("im the Result: " + this.state.stripdata)
return (
<div className="result">
<PersonCard />
</div>
);
}
}
export default Result;
Please help. This is blocking my progress.
Fix the issue here:
<Header onQuery={1}/>
{
(this.state.stripdata === null) ? <div className="loading">Loading data...</div> : <Content onResult={this.state.stripdata}/>
}
You need to check properties in state with name stripdata.
And btw, fetch has to be performed in ComponentDidMount, see https://daveceddia.com/where-fetch-data-componentwillmount-vs-componentdidmount/
The problem is that, in your Results, you are only using the value from props once: in the constructor, where you set to state.
You should not set value in state from props. Instead, just use the props directly. Change Result to as following, then it will work proper:
import React, { Component } from 'react'
import PersonCard from './PersonCard'
class Result extends Component {
constructor(props) {
super(props);
// removed setting state from props.stripdata
}
render() {
console.log("im the Result: " + this.props.stripdata) // <-- using props!
return (
<div className="result">
<PersonCard />
</div>
);
}
}
export default Result;
In general it is considered bad practice/antipattern to set state from props.
Related
2 components :- ClickCounter, mouseHoverCounter !
1 HOC component to do the counting work.
earlier I was counting the click and mouse hover by writing separate counter method in each component(cliccounter,mousehovecounter),
but
now, I'm trying to pass the component into hoc counter & get the new component with only one change , where I'm passing a props to originalComponent and returning it to see the behavior but its now working...
import React, { Component } from 'react'
import updatedComponent from './hocCounter'
class ClickCounter extends Component {
constructor(props) {
super(props)
this.state = {
counter:0
}
}
ClickCounterHandler = () =>{
this.setState((prevState)=>{
return {counter:prevState.counter+1}
})
}
render() {
const count=this.state.counter
return (
<div>
<button onClick={this.ClickCounterHandler}>{this.props.name} Clicked {count} Times</button>
</div>
)
}
}
export default updatedComponent(ClickCounter)
import React, { Component } from 'react'
import updatedComponent from './hocCounter'
class HoverMouseCounter extends Component {
constructor(props) {
super(props)
this.state = {
counter:0
}
}
MouseOverCounter(){
this.setState((prevState)=>{
return {counter:prevState.counter+1}
})
}
render() {
const count=this.state.counter
return (
<div>
<h1 onMouseOver={this.MouseOverCounter.bind(this)}>{this.props.name} Hovered For {count} Time(s)</h1>
</div>
)
}
}
export default updatedComponent(HoverMouseCounter)
import React from 'react'
const updatedComponent = originalComponent => {
class newComponent extends React.Component {
render(){
return <originalComponent name='Harsh'/>
}
}
return newComponent
}
export default updatedComponent
In App.js, I'm returning
<ClickCounter></ClickCounter>
<HoverMouseCounter></HoverMouseCounter>
this only !
Check the error in the console,
index.js:1 Warning: <originalComponent /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements. at originalComponent
This means You are using the small letter in originalComponent
React components are expected to start with a capital letter
Try this in you HOC component
import React from 'react'
const updatedComponent = OriginalComponent => {
class NewComponent extends React.Component {
render(){
return <OriginalComponent name='Harsh'/>
}
}
return NewComponent
}
export default updatedComponent
import React,{Component} from "react"
import axios from "axios"
import { List } from "#material-ui/core"
class PostList extends Component{
constructor(props){
super(props)
this.state={
posts:[]
}
}
componentDidMount(){
axios.get('http://localhost:8080/1706583/dummyServlet')
.then(response=>{
console.log(response.data)
this.setState({posts:response.data})
})
.catch(error =>{
console.log("error in post list"+error)
})
}
render(){
const {posts}=this.state
return(
<div>
List of posts{
posts.length ?
posts.map(post=><div key={post.acct_doc_header_id}>{post.company_id}</div>):
null
}
</div>
)
}
}
export default PostList
You need to initialize the state first, because when your request is still pending you have no array in the posts state.
And also make sure you get an array from the server. If the response is a string, you need to parse the JSON first using JSON.parse(myData);
class MyComponent extends React.Component {
state = {
posts: []
};
// ...
}
The below code works fine.
import React, { Component } from "react";
import axios from "axios";
import { List, ListItem } from "#material-ui/core";
class PostList extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(response => {
console.log(response.data);
this.setState({ posts: response.data });
})
.catch(error => {
console.log("error in post list" + error);
});
}
render() {
const { posts } = this.state;
return (
<div>
<List>
{posts.length
? posts.map(post => (
<ListItem key={post.id}>
{post.id}
{" - "}
{post.title}
</ListItem>
))
: null}
</List>
</div>
);
}
}
export default PostList;
I am getting "TypeError: undefined is not an object (evaluating 'this.props.tracks.map')" in the browser, although app compiles successfully.
I checked with console.log that both SearchResults and TrackList receive props (console.log prints all elements of array within map() method) so I do not understand why the object is undefined.
Code is presented below:
File App.js
import React from 'react';
import SearchResults from '../SearchResults/SearchResults.js'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [{name: '1'}, {name: '2'}]
}
}
render() {
return (
<div>
<div class="App">
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults} />
</div>
</div>
</div>
)
}
}
export default App;
File SearchResults.js
import React from 'react';
import TrackList from '../TrackList/TrackList.js'
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
<TrackList tracks={this.props.searchResults}/>
</div>
)
}
}
export default SearchResults;
File TrackList.js
import React from 'react';
import Track from '../Track/Track.js'
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{
this.props.tracks.map(track => {
return <Track track={track}/>
})
}
</div>
)
}
}
export default TrackList;
Update
I also added error from the browser:
Not exactly sure why this is happening, but when console logging this.props.tracks in Tracklist it is the defined, passed array from SearchResults then becomes undefined, thus causing the error you see.
QUICK FIX Use Guard Pattern
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
console.log(tracks); // two logs, first (2)[{...}, {...}], then undefined
return (
<div className="TrackList">
{
tracks && tracks.map((track, index) => { // Use a guard here to handle undefined tracks prop
return <Track key={index} track={track} />
})
}
</div>
)
}
}
In the above snippet you can simply place a guard on the tracks prop to defend against undefined values for the mapping. The more important question though is why this is occurring since the prop is never undefined here in SearchResult:
class SearchResults extends React.Component {
render() {
console.log('SearchResults', this.props.searchResults); // defined, only see 1 log entry
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList tracks={this.props.searchResults} />
</div>
)
}
}
FURTHER INVESTIGATION
I looked over the rest of your code and found that App is rendering PlayList, and PlayList also renders a TrackList but this time passes no tracks!
class Playlist extends React.Component {
render() {
return (
<div className="Playlist">
<input defaultValue={'New Playlist'} />
<TrackList /> // tracks becomes undefined here
<button className="Playlist-save">SAVE TO SPOTIFY</button>
</div>
)
}
}
SOLUTION Define Props/DefaultProps
At this point I would recommend using the react prop-types package to define your component's props and provide default values in some cases. Your TrackList component becomes:
import React from 'react';
import PropTypes from 'prop-types'; // import proptypes
import Track from '../Track/Track.js'
import './TrackList.css'
const propTypes = {
tracks: PropTypes.array // If you make this required (i.e. PropTypes.array.isRequired) then you'll get a react warning about missing props
};
const defaultProps = {
tracks: [], // if tracks props is unspecified this value is used
};
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
console.log(tracks);
return (
<div className="TrackList">
{
tracks.map((track, index) => { // no longer need guard
return <Track key={index} track={track} />
})
}
</div>
)
}
}
TrackList.propTypes = propTypes;
TrackList.defaultProps = defaultProps;
export default TrackList;
I have two component in my project one is Tag and the other is LandingTicker so i want when i click Tag componet update state for LandTicker componet, and landticker componet in different file.
how i can do that?
thank you.
Tag component code::
tag/index.js
import React from 'react';
import './index.scss';
class Tag extends React.Component {
handleClick(e) {
let tags = document.querySelectorAll('.show-clickable');
Array.from(tags).map(el => el.classList.remove('selected-tag'))
e.target.classList.add('selected-tag');
/*
Here i should update the state for LandingTicker component.
and remember any component in different file.
How i can do that???
*/
}
render() {
return (
<div
className="show-clickable"
onClick={this.handleClick}
>
click here
</div>
);
}
}
export default Tag;
LandingTicker component code::
LandingTicker/index.js
import React from 'react';
import TickerRow from './TickerRow';
import './index.scss';
class LandingTicker extends React.Component {
state = {
coin: 'USD'
}
render() {
return (
<div className="landing-ticker__body">
{selectCoin(this.state.coin)}
</div>
</div>
);
}
}
const selectCoin = (coin) => {
const coins = {
USD: ['BTCUSD', 'ETHUSD', 'EOSUSD', 'LTCUSD'],
EUR: ['BTCEUR', 'ETHEUR', 'EOSEUR'],
GBP: ['BTCGBP', 'EOSGBP'],
JPY: ['BTCJPY', 'ETHJPY'],
};
return (
coins[coin].map(el =>
<TickerRow symbol={el} key={el.toString()} />
)
);
}
export default LandingTicker;
Edit:
my component Hierarchy::
StatusTable
TagsTable
Tag
TickerSearch
LandingTickers
TickersRow
StatusTable component code::
import React from 'react';
import TagsTable from './TagsTable';
import TickerSearch from './TickerSearch';
import LandingTicker from './LandingTicker';
import './StatusTable.scss';
class StatusTable extends React.Component {
render() {
return (
<div className="status-table">
<TagsTable />
<TickerSearch />
<LandingTicker />
</div>
);
}
}
export default StatusTable;
React handle all its component data in the form of state and props(immutable). So it is easy to pass data from parent to child or one component to another using props :
Your Tag.js file:
import React, { Component } from "react";
import LandingTicker from "./LandTicker";
class Tag extends Component {
constructor(props) {
super(props);
this.state = {
trigger: true
};
}
handleClick(e) {
// do all logic here and set state here
this.setState({ trigger: this.state.trigger });
}
render() {
//And then pass this state here as a props
return (
<div className="show-clickable" onClick={this.handleClick}>
click here
<LandingTicker trigger={this.state.trigger} />
</div>
);
}
}
export default Tag;
Inside LandTicker.js file:
import React from 'react';
import TickerRow from './TickerRow';
import './index.scss';
class LandingTicker extends React.Component {
state = {
coin: 'USD'
}
render() {
//Catch your props from parent here
//i.e this.props(it contains all data you sent from parent)
return (
<div className="landing-ticker__body">
{selectCoin(this.state.coin)}
</div>
);
}
}
const selectCoin = (coin) => {
const coins = {
USD: ['BTCUSD', 'ETHUSD', 'EOSUSD', 'LTCUSD'],
EUR: ['BTCEUR', 'ETHEUR', 'EOSEUR'],
GBP: ['BTCGBP', 'EOSGBP'],
JPY: ['BTCJPY', 'ETHJPY'],
};
return (
coins[coin].map(el =>
<TickerRow symbol={el} key={el.toString()} />
)
);
}
export default LandingTicker;
I think this is the best answer for your question if you don't use state management system such as Redux or Mobx.
https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
(you need to check third option)
having a serious challenge with my React/Redux authorization setup and I'm at a loss. Current error is:
HomeHeader.js?8595:26 Uncaught ReferenceError: dispatch is not defined
There's a lot wrong here, and any help would be appreciated.
I'm creating two authorization components, one to register and one to login. The AuthorizationLogin component is what I'm working on first.
I don't know how to get this to pass the event info it grabs form the form fields, put in the variable creds, and then on up through the HomeHeader component, and then again up to the HomePage container. Do I put all of the authorization components into one enormous HomeHeader component?
Here's the main code and flow. Code isn't loading right into StackOverflow for some reason.
HomePage.js
import React from 'react';
import {connect} from 'react-redux';
import * as actions from '../../../actions/homeEventFormActions';
import HomeHeader from '../homeHeader/HomeHeader';
import HomeEventForm from '../homeEventForm/HomeEventForm';
class HomePage extends React.Component {
constructor(props) {
super(props);
}
render() {
const { dispatch } = this.props;
return (
<div>
< HomeHeader />
< HomeEventForm
onSubmit={this.props.onSubmit}
/>
</div>
);
}
}
function mapStateToProps(state) {
return {
homeEventForm: state.homeEventForm
};
}
function mapDispatchToProps(dispatch) {
return {
onSubmit: (eventName) => dispatch(actions.createEventButton(eventName)),
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomePage);
HomeHeader.js
import React, { Component, PropTypes } from 'react';
import AuthorizeLogin from '../../Authorization/AuthorizeLogin';
import AuthorizeRegister from '../../Authorization/AuthorizeRegister';
import { loginUser } from '../../../actions/authorizationActions';
class HomeHeader extends React.Component {
constructor() {
super();
}
_handleChange(eventKey) {
...
<AuthorizeLogin
onLoginClick={ (creds) => dispatch(loginUser(creds))}
/>
...
}
return;
}
render() {
const { dispatch } = this.props;
return (
...
<Modal.Header closeButton onClick={ ()=> this._handleChange(5)}>
...
);
}
}
export default HomeHeader;
AuthorizeLogin.js
import React from 'react';
class AuthorizeLogin extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {};
this._login = this._login.bind(this);
}
_login(e) {
e.preventDefault;
const email = this.refs.email;
const password = this.refs.password;
const creds = { email: email.value.trim(), password: password.value.trim() };
this.props.onLoginClick(creds);
}
render() {
return (
<Form horizontal onSubmit={this._login}>
<h4 className="authEmailText">Login with your email</h4>
<input type="email" placeholder="urawesome#example.com" ref='email' />
<input type="password" placeholder="Password" ref='password' />
</Form>
);
}
}
export default AuthorizeLogin;
All the actions should be dispatched from container. In this case, the container should have the dispatch.
function mapDispatchToProps(dispatch) {
return {
onSubmit: (eventName) => dispatch(actions.createEventButton(eventName)),
onLogin: (creds) => dispatch(loginUser(creds)),
};
}
The onLogin should be passed to downstream components via props:
<HomeHeader onLogin={this.props.onLogin} />
<AuthorizeLogin
onLoginClick={this.props.onLogin}
/>