React - passing object through props - reactjs

I am new to React and trying to pass object through attributes but getting following error.
Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {title}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of MeetingComponent.
Here is my code:
Main.jsx
import React from 'react';
import MeetingComponent from '../components/Meeting.jsx';
let meeting = {
title: 'Some title'
};
class AppComponent extends React.Component {
render() {
return (
<div className="index">
<MeetingComponent dataMeeting={meeting} />
</div>
);
}
}
AppComponent.defaultProps = {};
export default AppComponent;
Meeting.jsx
import React from 'react';
class MeetingComponent extends React.Component {
render() {
return (
<div>{this.props.dataMeeting}</div>
);
}
}
MeetingComponent.defaultProps = {};
export default MeetingComponent;
How can I solve this? What is the best practice?

The problem is here
<div>{this.props.dataMeeting}</div>
You cannot render an object in React, maybe you were trying to do
<div>{this.props.dataMeeting.title}</div>

If you want to pass properties of an object , it can be done as follows:
<MeetingComponent {...meeting} />
To access title of the meeting object inside the Meeting component, you can do it by calling it directly this.props.title
You can render it as <div>{this.props.title}</div>

i've used this method to pass object through component
let meeting = {
title: 'Some title'
};
class AppComponent extends React.Component {
render() {
const jsonData =JSON.stringify(meeting);
return (
<div className="index">
<MeetingComponent dataMeeting={jsonData } />
</div>
);
}
}
class MeetingComponent extends React.Component {
render() {
const data = JSON.parse(this.props.dataMeeting);
return (
<div>{data}</div>
<div>{data.title}</div>
);
}
}

static propTypes = {
pieChartSortSettings: PropTypes.shape({
type: PropTypes.string.isRequired,
order: PropTypes.string.isRequired,
}),
};
Here is a example how to pass object in props. You can also checkout methods like: PropTypes.arrayOfand PropTypes.objectOf(more: https://reactjs.org/docs/typechecking-with-proptypes.html)

Best practice is make your props as plain as possible, in your case it may be
<MeetingComponent title={ meeting.title } />
class MeetingComponent extends React.Component {
propTypes = {
title: React.PropTypes.string.isRequired
}
render() {
return (
<div>title: { this.props.title }</div>
);
}
}
And always use propTypes for better warning messages

Related

Usage of PropTypes in React js

i'm trying to pass data from a parent component to a child component via "props"
to do so, i've defined:
<Sidebar name="Dave" />
in the parent component.
and the following in the state :
message: this.props.name,
in the child component.
now i'm getting:
the error that i'm getting
now, i understood that i need to use PropTypes, for validation.
next thing that i did is to define:
SideBar.PropTypes = {
name: PropTypes.string,
}
as far as i know, it should be enough to fix the error,
could anyone please help me to figure this out?
the code of my Child Component: sideBar.jsx:
import PropTypes from 'prop-types'
class Sidebar extends React.Component {
constructor(props) {
super(props)
}
state = {
message: this.props.value,
}
render() {
return <div>hello {this.state.message}</div>
}
}
export default Sidebar
Sidebar.PropTypes = {
name: PropTypes.string,
}
the code of my Parent Component: Homepage.jsx:
class Homepage extends Component {
constructor(props) {
super(props)
}
render() {
return (
<>
<Sidebar name='Dave' />
</>
)
}
}
export default Homepage
The prop you passed is 'name', but the prop you used in your child component is 'value', so I think you should change your state as such:
state = {
message: this.props.name,
}

'Items' is not defined error when passing value to another component

I am getting ./src/App.js Line 27: 'Items' is not defined react/jsx-no-undef
while trying to pass state to a another component
import React, { Component } from 'react';
import axios from 'axios'
class App extends Component {
// Added this:
constructor(props) {
super(props);
// Assign state itself, and a default value for items
this.state = {
items: []
};
}
componentWillMount() {
axios.get('https://api.opendota.com/api/proMatches').then(res => {
this.setState({ items: res.data });
});
}
render() {
return (
<div className="app">
<Items items={this.state.items} />
</div></blink></blink>
);
}
}
export default App;
You are not trying to use a component called Items without importing it first:
<Items items={this.state.items} />
Every component that you use must be imported first:
import { Items } from "DIRECTORY"
Errors:- (1) import Items component (2) use componentDidMount() instead of componentWillMount() (3) use ternary operator in JSX this.state.items.length > 0 for displaying items after getting response
import React, { Component } from 'react';
import axios from 'axios';
import Items from './Items';
class App extends Component {
// Added this:
constructor(props) {
super(props);
// Assign state itself, and a default value for items
this.state = {
items: []
};
}
componentDidMount() {
axios.get('https://api.opendota.com/api/proMatches').then(res => {
this.setState({ items: res.data });
});
}
render() {
return (
<div className="app">
{
this.state.items.length > 0 ?
<Items items={this.state.items} /> : null
}
</div></blink></blink>
);
}
}
export default App;
App is top component and Items is child component.To use any child component or pass any kind props or state ,it has to be imported first .
There can be another issue to it since you are using state and if the child component Items is rerendered due to any change ,then state of your APP component will also reset.

passing an event to a child component in React

I'm new to React and this is a very noob question, but I don't understand why this is not working.
I'm trying to build a simple todo List.
My TodoList.js Component looks like this:
import React, {Component} from 'react';
import TodoItem from './TodoItem';
export default class TodoList extends Component{
constructor(props){
super(props);
this.state = {
todos:[
{
title:"todo1"
},
{
title:"todo3"
},
{
title:"todo2"
}
]
}
}
handleRemove(idx){
alert('works');
}
render(){
var todos = this.state.todos.map(function(t,idx){
return(<TodoItem
remove={this.handleRemove.bind(this,idx)}
title={t.title}
/>)
})
return (
<div>
<h1>To do</h1>
<div>{todos}</div>
</div>
)
}
}
My child Component looks like this:
import React, {Component} from 'react';
export default class TodoItem extends Component{
render(){
return (
<div>{this.props.title}
<button onClick={this.props.remove}>X</button>
</div>
)
}
}
But I get a TypeError with "Cannot read property 'handleRemove' of undefined". I'm wondering why inside the map function {this} is undefined?
I tried to put this this.handleRemove = this.handleRemove.bind(this) into the constructor.
Didn't change anything. Shouldn't this also be defined inside the .map() ?
You need to put this as the second argument
If a thisArg parameter is provided to map, it will be used as
callback's this value. Otherwise, the value undefined will be used as
its this value. The this value ultimately observable by callback is
determined according to the usual rules for determining the this seen
by a function.
on map:
render(){
var todos = this.state.todos.map(function(t,idx){
return(<TodoItem
remove={this.handleRemove.bind(this,idx)}
title={t.title}
/>)
}, this)
return (
<div>
<h1>To do</h1>
<div>{todos}</div>
</div>
)
}
}
Alternatively, you can use an ES6 arrow function to automatically preserve the current this context:
var todos = this.state.todos.map((t,idx) => {
return(<TodoItem
remove={this.handleRemove.bind(this,idx)}
title={t.title}
/>)
})

How to include the Match object into a ReactJs component class?

I am trying to use my url as a parameter by passing the Match object into my react component class. However it is not working! What am I doing wrong here?
When I create my component as a JavaScript function it all works fine, but when I try to create my component as a JavaScript class it doesn't work.
Perhaps I am doing something wrong? How do I pass the Match object in to my class component and then use that to set my component's state?
My code:
import React, { Component } from 'react';
import axios from 'axios';
import PropTypes from 'prop-types';
class InstructorProfile extends Component {
constructor(props, {match}) {
super(props, {match});
this.state = {
instructors: [],
instructorID : match.params.instructorID
};
}
componentDidMount(){
axios.get(`/instructors`)
.then(response => {
this.setState({
instructors: response.data
});
})
.catch(error => {
console.log('Error fetching and parsing data', error);
});
}
render(){
return (
<div className="instructor-grid">
<div className="instructor-wrapper">
hi
</div>
</div>
);
}
}
export default InstructorProfile;
React-Router's Route component passes the match object to the component it wraps by default, via props. Try replacing your constructor method with the following:
constructor(props) {
super(props);
this.state = {
instructors: [],
instructorID : props.match.params.instructorID
};
}
Hope this helps.
Your constructor only receives the props object, you have to put match in it...
constructor(props) {
super(props);
let match = props.match;//← here
this.state = {
instructors: [],
instructorID : match.params.instructorID
};
}
you then have to pass that match object via props int a parent component :
// in parent component...
render(){
let match = ...;//however you get your match object upper in the hierarchy
return <InstructorProfile match={match} /*and any other thing you need to pass it*/ />;
}
for me this was not wrapping the component:
export default (withRouter(InstructorProfile))
you need to import withRouter:
import { withRouter } from 'react-router';
and then you can access match params via props:
someFunc = () => {
const { match, someOtherFunc } = this.props;
const { params } = match;
someOtherFunc(params.paramName1, params.paramName2);
};
Using match inside a component class
As stated in the react router documentation. Use this.props.match in a component class. Use ({match}) in a regular function.
Use Case:
import React, {Component} from 'react';
import {Link, Route} from 'react-router-dom';
import DogsComponent from "./DogsComponent";
export default class Pets extends Component{
render(){
return (
<div>
<Link to={this.props.match.url+"/dogs"}>Dogs</Link>
<Route path={this.props.match.path+"/dogs"} component={DogsComponent} />
</div>
)
}
}
or using render
<Route path={this.props.match.path+"/dogs"} render={()=>{
<p>You just clicked dog</p>
}} />
It just worked for me after days of research. Hope this helps.
In a functional component match gets passed in as part of props like so:
export default function MyFunc(props) {
//some code for your component here...
}
In a class component it's already passed in; you just need to refer to it like this:
`export default class YourClass extends Component {
render() {
const {match} = this.props;
console.log(match);
///other component code
}
}`

React cloneElement and component instance

I have the following higher order component that I am trying to wrap in a container element that is supplied as a prop:
import React, { PropTypes } from 'react';
export default (Component) => {
return class extends React.Component {
static propTypes = {
containerElement: PropTypes.element
}
static defaultProps = {
containerElement: <div />
};
componentDidMount() {
console.log(this.el);
}
render() {
const containerProps = {
ref: (el) => this.el = el
};
return React.cloneElement(containerElement, containerProps, Component);
};
}
}
I then wrap a component like this:
export default AnimationComponent(reduxForm({
form: 'newResultForm',
validate
})(NewResultForm));
But when I log the element in componentDidMount it is an empty <div/>.
Why is the passed in component not a child of the newly created container element?
Your method of writing a Higher Order Component is a little unorthodox. React developers typically don't have to write functions that accept components and return a new class definition unless they're writing something like redux-form itself. Perhaps instead of passing Component as an argument, see if passing it in props.children will work for you:
<AnimationComponent>{NewResultForm}</AnimationComponent>
I'd define AnimationComponent like the following:
export default class AnimationComponent extends React.Component {
static propTypes = {
containerElement: React.PropTypes.element
};
static defaultProps = {
containerElement: <div />
};
render () {
// For each child of this component,
// assign each a ref and store it on this component as this[`child${index}`]
// e.g. this.child1, this.child2, ...
// Then, wrap each child in the container passed in on props:
return React.Children.map(this.props.children, (child, index) =>
React.cloneElement(
this.props.containerElement,
{ref: ref => this[`child${index}`] = ref},
React.cloneElement(child)
)
);
}
}
Instead of wrapping the form component in AnimationComponent, just export the connected form class:
export default reduxForm({
form: 'newResultForm',
validate
})(NewResultForm));
Now instead of being stuck with how AnimationComponent was configured in NewResultForm's file, we can configure it to our liking where we end up rendering the form. In addition to providing flexibility, the information needed to configure AnimationComponent will be more pertinent where it gets rendered:
export default class MyApp extends React.Component {
render() {
return (
<AnimationComponent containerComponent="span">
<NewResultForm />
</AnimationComponent>
);
}
}
I hope this helped!

Resources