Reactjs this.props.map is not a function - reactjs

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: []
}
]
};
}

Related

React props wont change manually, how to get around restrictions?

When the component renders it has some children components and when i log props in the formgroup it looks like what's below.
{$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: ƒ, …}
import React from 'react';
class FormGroup extends React.Component {
constructor(props) {
super(props)
this.state = {
value: {}
}
}
setValue() {
//do nothing at the moment
}
render() {
return (<div className="formGroup" >
{
this.props.children.map(child => {
child.props.setValue = this.setValue;
console.log('child', child)
// does not change
return child;
})
}
</div>)
}
};
export default FormGroup;
You can utilize React Children and cloneElement API to manipulate children's props.
import React from 'react';
class FormGroup extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {},
};
}
setValue() {
//do nothing at the moment
}
render() {
const { children } = this.props;
return (
<div className="formGroup">
{React.Children.map(children, (child) =>
React.cloneElement(child, {
...child.props,
setValue: this.setValue,
})
)}
</div>
);
}
}
export default FormGroup;

this.state not showing in html

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

Undefined props in componentDidMount

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

create separate react component for cookie management

I have a component which renders if its prop.paramA is different from paramA value stored in cookie.
Following is my attempt to move the cookie management logic into a separate component. The problem is that the question component gets rendered only once when showQuestion is false so I never get to see the question.
Currently, my code looks like below
// question.js
import React from 'react';
import ToggleDisplay from 'react-toggle-display';
import {withCookies, Cookies} from 'react-cookie';
class Question extends React.Component {
static get propTypes() {
return {
cookies: instanceOf(Cookies).isRequired
}
}
constructor(props) {
super(props);
this.state = {
paramA: props.paramA,
showQuestion: props.showQuestion
};
}
componentDidMount() {
if (this.state.showQuestion) {
// do some ajax calls etc.
}
}
render() {
return (
<div id="simplequestion">
<ToggleDisplay show={this.state.showQuestion}>
<div>What is your SO profile?</div>
</ToggleDisplay>
</div>
);
}
}
export default withCookies(Question);
I wrote following CookieManager component -
// cookiemanager.js
import React from 'react';
import {withCookies, Cookies} from 'react-cookie';
class CookieManager extends React.Component {
static get propTypes() {
return {
cookies: instanceOf(Cookies).isRequired
}
}
constructor(props) {
super(props);
let propA = props.cookies.get('propA');
if (!propA || propA !== props.propA) {
props.cookies.set('propA', props.propA, { path: '/', maxAge: 604800});
console.log('changed', propA, props.propA);
props.propAChanged(true);
} else if (propA === props.propA) {
console.log('unchanged', propA, props.propA);
props.propAChanged(false);
}
}
render() {
return <div id="cookieManager"></div>;
}
}
export default withCookies(CookieManager);
Following is the top level app
// app.js
import React from 'react';
import Question from './question';
import CookieManager from './cookiemanager';
import { CookiesProvider } from 'react-cookie';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
paramA: props.paramA,
showQuestion: false,
avoidUpdate: false
};
this.updateShowQuestion = this.updateShowQuestion.bind(this);
}
updateShowQuestion(x) {
if (this.state.avoidUpdate) {
return;
}
this.setState({
paramA: this.state.paramA,
showQuestion: x,
avoidUpdate: !this.state.avoidUpdate
});
}
componentWillUpdate(nextProps, nextState) {
if (!nextState.avoidUpdate && nextState.paramA === this.state.paramA) {
this.setState({
paramA: this.state.paramA,
showQuestion: this.state.showQuestion,
avoidUpdate: true
});
}
}
render() {
return (
<CookiesProvider>
<CookieManager paramA={this.state.paramA} ridChanged={this.updateShowQuestion}>
<Question
paramA={this.state.paramA}
showQuestion={this.state.showQuestion}/>
</CookieManager>
</CookiesProvider>
);
}
}
export default App;
You're reading from state here:
<ToggleDisplay show={this.state.showQuestion}>
But showQuestion is set only once, in the constructor:
this.state = {
paramA: props.paramA,
showQuestion: props.showQuestion
};
When this.props changes your component is rendered, but you're still reading from this.state which is not updated, so the visible output is the same.
I see two ways you can solve this:
Update this.state.showQuestion whenever the props change:
static getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.showQuestion !== prevState.showQuestion)
return { showQuestion };
}
In render(), read from this.props.showQuestion:
<ToggleDisplay show={this.props.showQuestion}>
This way may look easier but with the other you gain more fine-grained control over state changes.
Another thing you've missed is found here:
componentWillUpdate(nextProps, nextState) {
if (!nextState.avoidUpdate && nextState.paramA === this.state.paramA) {
this.setState({
paramA: this.state.paramA,
showQuestion: this.state.showQuestion, // this line
avoidUpdate: true
});
}
}
You're receiving new props, but you're setting the old state: showQuestion: this.state.showQuestion. Instead, set the new showQuestion value from the new props - nextProps, this way:
componentWillUpdate(nextProps, nextState) {
if (!nextState.avoidUpdate && nextState.paramA === this.state.paramA) {
this.setState({
paramA: this.state.paramA,
showQuestion: nextProps.showQuestion, // this line
avoidUpdate: true
});
}
}

Error Handle and Log the errors in React js

I'm trying to handle the error and logging the errors in react js
I used errorBoundary method to handle the error but it's only support for the react js version 16
import React, {Component} from "react";
import "./App.css";
import errorimg from './errorimg.svg';
//create a erro boundry
class ErrorLimit extends Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}
//set the error value when invoked
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
})
logErrorToMyService(error, info);
}
render() {
//checked the error
if (!!this.state.errorInfo) {
return (
<div className="snap">
<img src= {errorimg}/>
<div className="snap-message">
{this.state.error && this.state.error.toString()}
{this.state.errorInfo.componentStack}
<p> <b>Error occured - something's gone wrong.</b></p>
<p>Anyway we handled error
</p>
</div>
</div>
);
} else {
return this.props.children;
}
}
}
class Widget extends Component {
constructor(props) {
super(props);
this.state = { loading: true, n: 0 };
this.getCount = this.getCount.bind(this)
}
getCount() {
if (this.state.n > 3) throw new Error('woops..counter betes limit');
return `(${this.state.n})`;
}
handleClick() {
this.setState({ n: this.state.n + 1 });
}
render() {
return (
<div>
<div>Counter widget {this.getCount(this.state.n)}</div>
<button onClick={this.handleClick.bind(this)}>
Click me a few times
</button>
</div>
);
}
}
class SampleApp extends Component {
render() {
return (
<div className="sidebar">
{/* here we used error boundry */}
<ErrorLimit>
<Widget />
</ErrorLimit>
</div>
);
}
}
class App extends Component {
render() {
return (
<div className="app">
<SampleApp />
</div>
);
}
}
export default App;
and I have tried another method for react version 15, It's work fine. Successfully handle the error but can't log the error
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import wrapWithTryCatch from 'react-try-catch-render';
import errorimg from './errorimg.svg';
class MyErrorHandler extends React.Component {
render(){
return (
<div className="App-header">{this.props.error}</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true, n: 0 };
this.getCount = this.getCount.bind(this)
this.handleClick = this.handleClick.bind(this);
}
getCount() {
if (this.state.n > 3) throw new Error('woops..counter betes limit');
return `(${this.state.n})`;
}
handleClick() {
this.setState({ n: this.state.n + 1 });
}
render(){
return(
<div>
<div>Counter widget {this.getCount(this.state.n)}</div>
<button onClick={this.handleClick.bind(this)}>
Click me a few times
</button>
</div>
);
}
}
export default wrapWithTryCatch(React, MyErrorHandler, {error: "Error catched!"})(App);
Anyone please suggest a method to handle the error and log the error in react version 15
React versions 16.0 and above support error boundaries which can handle the errors.
Below React 16.0, you need to handle errors by state like:
// Create a state variable for error. The initial state is false because there are no errors.
this.state = { hasError: false };
// Set hasError as true when an error is detected.
this.setState({ hasError: true });
if (this.state.hasError){
// Do something, e.g. handle error.
}

Resources