Most likely I'm doing this wrong, when I console.log this.state.name and email it shows up in the console but when I render it doesn't show up. I'm vey new to react so please excuse the code, if there is a better method of doing this please show me. what Im trying to do here is fetch a profile page from an axios.get request (profilePage) and display this data on the page
import React, { Component } from 'react';
import { profilePage } from '../UserFunctions'
export default class Profile extends Component {
constructor() {
super();
//Set default message
this.state = {
param: null,
message: 'Loading...',
name: '',
email: ''
}
}
componentDidMount() {
let Paramvalue=this.props.match.params.id;
this.state.param = Paramvalue
var user = this.state.param
profilePage(user).then(res => {
this.state.name = res.data[0].fname + ' ' + res.data[0].lname
this.state.email = res.data[0].email
console.log(this.state.name)
})
}
render() {
return (
<div>
<h1>Home</h1>
<h1>{this.state.name}</h1>
<h1>{this.state.email}</h1>
</div>
);
}
}
this is because you are directly mutating the state object instead of calling setState so a re-render is not triggered. Here is a modified version of your code which should work as expected.
import React, { Component } from 'react';
import { profilePage } from '../UserFunctions'
export default class Profile extends Component {
constructor() {
super();
//Set default message
this.state = {
param: null,
message: 'Loading...',
name: '',
email: ''
}
}
componentDidMount() {
let user=this.props.match.params.id;
profilePage(user).then(res => {
this.setState({
name: res.data[0].fname + ' ' + res.data[0].lname,
email: res.data[0].email,
param: user,
});
})
}
render() {
return (
<div>
<h1>Home</h1>
<h1>{this.state.name}</h1>
<h1>{this.state.email}</h1>
</div>
);
}
}
Related
Here are my components:
App component:
import logo from './logo.svg';
import {Component} from 'react';
import './App.css';
import {MonsterCardList} from './components/monster-list/monster-card-list.component'
import {Search} from './components/search/search.component'
class App extends Component
{
constructor()
{
super();
this.state = {searchText:""}
}
render()
{
console.log("repainting App component");
return (
<div className="App">
<main>
<h1 className="app-title">Monster List</h1>
<Search callback={this._searchChanged}></Search>
<MonsterCardList filter={this.state.searchText}></MonsterCardList>
</main>
</div>
);
}
_searchChanged(newText)
{
console.log("Setting state. new text: "+newText);
this.setState({searchText:newText}, () => console.log(this.state));
}
}
export default App;
Card List component:
export class MonsterCardList extends Component
{
constructor(props)
{
super(props);
this.state = {data:[]};
}
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
_loadData(monsterCardCount)
{
fetch("https://jsonplaceholder.typicode.com/users", {
method: 'GET',
}).then( response =>{
if(response.ok)
{
console.log(response.status);
response.json().then(data => {
let convertedData = data.map( ( el, index) => {
return {url:`https://robohash.org/${index}.png?size=100x100`, name:el.name, email:el.email}
});
console.log(convertedData);
this.setState({data:convertedData});
});
}
else
console.log("Error: "+response.status+" -> "+response.statusText);
/*let data = response.json().value;
*/
}).catch(e => {
console.log("Error: "+e);
});
}
render()
{
console.log("filter:" + this.props.filter);
return (
<div className="monster-card-list">
{this.state.data.map((element,index) => {
if(!this.props.filter || element.email.includes(this.props.filter))
return <MonsterCard cardData={element} key={index}></MonsterCard>;
})}
</div>
);
}
}
Card component:
import {Component} from "react"
import './monster-card.component.css'
export class MonsterCard extends Component
{
constructor(props)
{
super(props);
}
render()
{
return (
<div className="monster-card">
<img className="monster-card-img" src={this.props.cardData.url}></img>
<h3 className="monster-card-name">{this.props.cardData.name}</h3>
<h3 className="monster-card-email">{this.props.cardData.email}</h3>
</div>
);
}
}
Search component:
import {Component} from "react"
export class Search extends Component
{
_searchChangedCallback = null;
constructor(props)
{
super();
this._searchChangedCallback = props.callback;
}
render()
{
return (
<input type="search" onChange={e=>this._searchChangedCallback(e.target.value)} placeholder="Search monsters"></input>
);
}
}
The problem is that I see how the text typed in the input flows to the App component correctly and the callback is called but, when the state is changed in the _searchChanged, the MonsterCardList seems not to re-render.
I saw you are using state filter in MonsterCardList component: filter:this.props.searchText.But you only pass a prop filter (filter={this.state.searchText}) in this component. So props searchTextis undefined.
I saw you don't need to use state filter. Replace this.state.filter by this.props.filter
_loadData will get called only once when the component is mounted for the first time in below code,
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
when you set state inside the constructor means it also sets this.state.filter for once. And state does not change when searchText props change and due to that no rerendering.
constructor(props)
{
super(props);
this.state = {data:[], filter:this.props.searchText};
}
If you need to rerender when props changes, use componentDidUpdate lifecycle hook
componentDidUpdate(prevProps)
{
if (this.props.searchText !== prevProps.searchText)
{
this._loadData();
}
}
Well, in the end I found what was happening. It wasn't a react related problem but a javascript one and it was related to this not been bound to App class inside the _searchChanged function.
I we bind it like this in the constructor:
this._searchChanged = this._searchChanged.bind(this);
or we just use and arrow function:
_searchChanged = (newText) =>
{
console.log("Setting state. new text: "+newText);
this.setState({filter:newText}, () => console.log(this.state));
}
Everything works as expected.
I'm trying to make a simple message app that takes the users name, and a message, then the messages are passed down and displayed in another component. In the component that should display the messages I'm getting an error saying this.props.messages.map is not a function.
Here is my code sandbox:
https://codesandbox.io/s/unruffled-pasteur-nz32o
And here is my actual code:
Parent component:
import React, { Component } from "react";
import Messages from "./Messages";
import Input from "./Input";
export default class Container extends Component {
constructor(props) {
super(props);
this.state = {
messages: {
user: [],
message: []
}
};
}
updateMessage(message, sender) {
this.setState({
messages: [...this.state.messages, { user: sender, message: message }]
});
}
render() {
return (
<div>
<Messages messages={this.state.messages} />
<Input
updateMessage={(message, sender) =>
this.updateMessage(message, sender)
}
/>
</div>
);
}
}
And here is where the messages should be displayed (and also where I am getting the error):
import React, { Component } from "react";
export default class Messages extends Component {
render() {
return this.props.messages.map(message => {
return (
<div>
{message.user}: {message.maessage}
</div>
);
});
}
}
Any ideas what could be causing my error? Thanks!
messages is initialized as an object in state. If you want to map over it, it should be an array. So rather than this:
constructor(props) {
super(props);
this.state = {
messages: {
user: [],
message: []
}
};
}
You'll, want this:
constructor(props) {
super(props);
this.state = {
messages: [
{
user: [],
message: []
}
]
};
}
This is starting to get really frustrating. Basically, I cannot access props in my subcomponents. if I try to render them directly using this.props- it works, but if I need to do additional processes with them, or save them into state, I get undefined props all the time. I have a parent component, which looks something like this:
import React from 'react';
import Title from './EventSubComponents/Title';
import SessionInfo from './EventSubComponents/SessionInfo';
import SessionTime from './EventSubComponents/SessionTime';
import Location from './EventSubComponents/Location';
import Subscribers from './EventSubComponents/Subscribers';
class EventNode extends React.Component {
constructor(props) {
super(props);
this.state = {
'event': [],
}
}
componentDidMount() {
this.getEvent(this.props.location.selectedEventId);
}
getEvent(eventId) {
fetch('/api/v.1.0/event/' + eventId, {mode: 'no-cors'})
.then(function(response) {
if(!response.ok) {
console.log('Failed to get single event.');
return;
}
return response.json();
})
.then((data) => {
if (!data) {
return;
}
this.setState({
'event': data
})
});
}
render() {
return(
<div className="event-wrapper">
<Title
title = { this.state.event.title }
date = { this.state.event.start }
/>
<SessionInfo
distance = { this.state.event.distance }
type = { this.state.event.type }
/>
<SessionTime
start = { this.state.event.start }
end = { this.state.event.end }
/>
<Location location = { this.state.event.start_location }/>
<Subscribers
subscribers = { this.state.event.subscribers }
eventId = { this.state.event._id }
/>
</div>
);
}
}
export default EventNode;
And my sub-component SessionTime, which looks like this:
import React from 'react';
import moment from 'moment';
class Title extends React.Component {
constructor(props) {
super(props);
this.state = {
'title': '',
'date': '',
}
}
componentDidMount() {
console.log(this.props.title);
console.log(this.props.date);
// undefined both props.
this.convertToTitleDate(this.props.date);
this.setState({
'title': this.props.title
})
}
convertToTitleDate(date) {
var newDate = moment(date).format('dddd, Do MMMM')
this.setState({
'date': newDate,
});
}
render() {
return (
<div className="event-title-wrapper">
<h1> { this.state.title } </h1>
<div className="event-title-date"> { this.state.date } </div>
</div>
);
}
}
export default Title;
Could anyone explain, why both this.props.date and this.props.title are undefined in my componentDidMount function? I have couple more components in my EventNode and I have the same problems in them as well.
Changing componentDidMount to componentWillMount does not help. I am fairly certain I have problems in my parent EventNode component, but I cannot figure out where. Inside EventNode render() all the state variables are defined.
You initialize event to an empty array and pass down this.state.event.start and this.state.event.end to SessionTime, which will both be undefined on first render since event has not been loaded yet and there are no start and end properties on the array.
You could instead e.g. set event to null initially, and return null from the render method until the event has been loaded.
Example
class EventNode extends React.Component {
state = {
event: null
};
// ...
render() {
const { event } = this.state;
if (event === null) {
return null;
}
return (
<div className="event-wrapper">
<Title title={event.title} date={event.start} />
<SessionInfo distance={event.distance} type={event.type} />
<SessionTime start={event.start} end={event.end} />
<Location location={event.start_location} />
<Subscribers
subscribers={event.subscribers}
eventId={this.state.event._id}
/>
</div>
);
}
}
I'm fetching Data from a Dribbble API. It is almost succefully but it is loading 12 shots from the API, and after this it is occurring a problem by repeating the same 12 again and then it after this it is loading the others in a right way: 13,14,15...
The shots' return:
1,2,3,4,5,6,7,8,9,10,11,12.
OK RIGHT
1,2,3,4,5,6,7,8,9,10,11,12.
repetition problem!!! WRONG
13,14,15,16... OK RIGHT
What is the solution for this undesirable repetition?
Here is the code:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Waypoint from 'react-waypoint';
import Dribbble from './Dribbble';
export default class Dribbbles extends Component {
constructor(props) {
super(props);
this.state = { page: 1, shots: [] };
this.getShots = this.getShots.bind(this);
}
componentDidMount() {
this.getShots();
}
getShots() {
return $.getJSON('https://api.dribbble.com/v1/shots?page='+ this.state.page + '&access_token=ACCESS_TOKEN_HERE&callback=?')
.then((resp) => {
var newShots = this.state.shots.concat(resp.data);
this.setState({
page: this.state.page + 1,
shots: newShots
});
});
}
render() {
const shots = this.state.shots.map((val, i) => {
return <Dribbble dados={val} key={i} />
});
return (
<div>
<ul>{shots}</ul>
<Waypoint
onEnter={this.getShots}
/>
</div>
);
}
}
Why
I think the onEnter event from your Waypoint component is triggered unexpectedly, which has caused the getShots() function being called multiple times.
How
My suggestion is to specify a dataFecthed state to control whether the Waypoint component mount or not.Just like below:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Waypoint from 'react-waypoint'
import Dribbble from './Dribbble'
export default class Dribbbles extends Component {
constructor(props) {
super(props)
this.state = { page: 1, shots: [], dataFetched: true }
this.getShots = this.getShots.bind(this)
}
componentDidMount() {
this.getShots()
}
getShots() {
this.setState({
dataFetched: false,
})
return $.getJSON(
'https://api.dribbble.com/v1/shots?page=' +
this.state.page +
'&access_token=41ff524ebca5e8d0bf5d6f9f2c611c1b0d224a1975ce37579326872c1e7900b4&callback=?'
).then(resp => {
var newShots = this.state.shots.concat(resp.data)
this.setState({
page: this.state.page + 1,
shots: newShots,
dataFetched: true,
})
})
}
render() {
const shots = this.state.shots.map((val, i) => {
return <Dribbble dados={val} key={i} />
})
return (
<div>
<ul>{shots}</ul>
{this.state.dataFetched && <Waypoint onEnter={this.getShots} />}
</div>
)
}
}
This is my App.js and i have set my database in firebase. All the messages which i enter all display in database also.But i need to automatically send message back to me . so any one knows how to do that please help. Thank you.
import React, { Component } from 'react';
import MessagePane from './MessagePane';
import ChannelList from './ChannelList';
import { getMessages, getChannels, saveMessage, onNewMessage } from './remote_storage1';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
messages: [],
channels: [],
selected_channel_id: null
};
this.onSendMessage = this.onSendMessage.bind(this);
this.onChannelSelect = this.onChannelSelect.bind(this);
this.filteredMessages = this.filteredMessages.bind(this);
}
componentDidMount() {
getMessages().then(messages => this.setState({messages}));
getChannels().then(channels => this.setState({channels, selected_channel_id: channels[0].id}));
onNewMessage(new_message => {
const messages = [...this.state.messages, new_message];
this.setState({messages});
});
}
onSendMessage(author, text) {
const new_message = {
id: this.state.messages[this.state.messages.length - 1].id + 1,
author,
text,
channel_id: this.state.selected_channel_id
};
saveMessage(new_message);
const messages = [...this.state.messages, new_message];
this.setState({messages});
}
onChannelSelect(id) {
this.setState({ selected_channel_id: id });
}
filteredMessages() {
return this.state.messages.filter(({channel_id}) => channel_id === this.state.selected_channel_id);
}
render() {
return (
<div className="App">
<ChannelList
channels={this.state.channels}
selectedChannelId={this.state.selected_channel_id}
onSelect={this.onChannelSelect}
/>
<MessagePane messages={this.filteredMessages()} onSendMessage={this.onSendMessage} />
</div>
);
}
}
export default App;