How to Pass Object Values to Children in ReactJS - reactjs

I am trying to pass an object from Parent to Child as described in this article.
I am new to React JS and am probably missing something simple here.
The goal of this is to have a select list of "Industries" that passes the selected industry to a select list of "Categories". The selected Category would then populate a table.
I am getting the error that this.props.selectedIndustry doesn't exist on compile.
Here is my code:
class CategoryFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
industrySelect: this.props.selectedIndustry
}
console.log(this.props.selectedIndustry)
}
render() {
const { selectedCategory } = this.state;
const categories = utmcategories.filter(function(i) {return i.industry==this.props.selectedIndustry})
return (
<Select options={categories} onChange={this.handleCategoryChange}
/>
);
}
class IndustryFilter extends React.Component {
state = {
selectedIndustry: null,
};
handleIndustryChange = (selectedIndustry) => {
this.setState({ selectedIndustry });
console.log(`Option selected:`, selectedIndustry);
}
render() {
const { selectedIndustry } = this.state;
const industries = utmindustries;
return (
<div>
<Select options={industries}
onChange={this.handleIndustryChange}
autoFocus={true}
/>
<CategoryFilter selectedIndustry={selectedIndustry}/>
</div>
);
}
}
I modified the code to narrow down the issue and made my CategoryFilter this:
function CategoryFilter(props) {
return (
<div>
{props.filteredIndustry}
</div>
)
}
But still got the error:
"Objects are not valid as a React Child."

You are initialising selectedIndustry to null here:
state = {
selectedIndustry: null,
};
and then passing it in as a prop to <CategoryFilter>
I am getting the error that this.props.selectedIndustry doesn't exist
on compile. Here is my code
Not quite sure that the 'error' you are getting is actually an error. Seems like it may be the expected behaviour. Could you please show us the error message exactly?
I would expect this.props.selectedIndustry to be defined as null after <CatergoryFilter> has mounted. You should try initialising it to a value e.g. "Default Industry" and see if that console.log(this.props.selectedIndustry) in the constructor is printing "Default Industry". If not, I would suspect that the this context has not been initialised until after the constructor has returned. Try logging this value in the render() method instead.

Related

Calling props from a container

I am a little confused on the idea of using props in the context I am using for my React app. In my component, I need to check if the value of a certain prop (props.companyCode) matches a certain string, and only then will it print out a <p> of what I need. Below is what I have for calling the prop in the component:
Components/CompanyContact.jsx
class CompanyContact extends React.Component {
help() {
if (this.props.companyInfoList.companyCode === '1234') {
return <p>something</p>;
}
return <p>somethingelse</p>;
}
render() {
const help = this.help();
return (
<div>
{help};
</div>
)}}
export default CompanyContact;
And this is what I have for the container:
Container/InfoContainer.jsx
class InfoContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
companyInfoList: null,
};
}
async componentWillMount() {
const companyInfoCachedData = CachingService.getData('companyInfoList');
if (companyInfoCachedData) {
this.setState({ companyInfoList: companyInfoCachedData });
return;
}
}
async getCompanyInfo(accessToken) {
try {
const companyProfileResponse = await requestAWSGet('api/company-profile', undefined, accessToken);
CachingService.setData('companyInfoList', companyProfileResponse);
this.setState({ companyInfoList: companyProfileResponse });
} catch (err) {
throw err;
}
}
render() {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
}
}
export default InfoContainer;
Nothing is returned when I run the application and I believe it's because I'm not calling the prop correctly in my component but I am unsure as to how to go about fixing it. I'm fairly new to working with props so still trying to get my bearings.
I'm assuming you are getting an error somewhere because of this not having props and this.props.companyInfoList.companyCode trying to access a property on a non object. this.props.companyInfoList is initially set to null so accessing a property on it will break.
A few strategies to fix the problem:
Default it to an empty object
this.state = {
companyInfoList: {},
}
Block the rendering of the component until it has a value:
if (this.state.companyInfoList) {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
} else {
return null;
}
Check that the prop is an object and has the key companyCode on it:
if (this.props.companyInfoList &&
this.props.companyInfoList.companyCode &&
this.props.companyInfoList.companyCode === '1234') {
In addition, this will be in the wrong context and the changes above will most likely no be enough. Try changing to an arrow function like this:
help = () => {
// your code here
}
I would personally refactor that component logic and directly use the prop value inside the render method like:
class CompanyContact extends React.Component {
render() {
const { companyInfoList } = this.props;
return companyInfoList && companyInfoList.companyCode === '1234' ? (
<p>something</p>
) : (
<p>somethingelse</p>
)
}
}
export default CompanyContact;

this.state is null inside render React and Typescript

I am a novice in ReactJS. Was watching a tutorial that is recorded in ES6 JavaScript and simultaneously I am trying to recreate the same app in TypeScript (I am a novice in TypeScript too!!). In ES6 we need to use a class based approach if that particular component needs to maintain it's own state. I have installed "babel-plugin-transform-class-properties" so i do set state in ES6 as export default class SomeClass { state = {
someProperty : someValue }}. But in TypeScript I am using class based approach for every component. So here is a component that tries to maintain it's own state:
import React from 'react';
interface IAddOptionProps {
handleAddOption: (option: string) => string |null;
};
interface IAddOptionState {
error: any;
};
export default class AddOption extends React.Component<IAddOptionProps, IAddOptionState> {
handleAddOption = (e:any) => {
e.preventDefault();
const option = e.target.elements.option.value.trim();
const err : string = this.props.handleAddOption(option);
this.setState(() => {
return {
error: err
}
});
if (!err) {
e.target.elements.option.value = '';
}
}
render() {
console.log(this.state);
return (
<div>
{this.state.error != null ? <p>{this.state.error}</p> : ''}
<form onSubmit={this.handleAddOption}>
<div className="form-group">
<label>Enter an Option</label>
<input type="text" className="form-control" id="option" name="option"/>
</div>
<button className="btn btn-primary">Add option</button>
</form>
</div>
);
}
};
The statement console.log(this.state); and {this.state.error != null ? <p>{this.state.error}</p> : ''} inside render() is throwing error stating that Uncaught TypeError: Cannot read property 'error' of null. That means this.state is being set as null.
Why is state getting set to null and how do I resolve this ( In TypeScript )?
Thanks in Advance.
As already mentioned in the comments, you have to initialize the state either in the constructor or with a property initializer like this:
class AddOption extends React.Component<IAddOptionProps, IAddOptionState> {
this.state = {
error: ''
};
[...]
}
Otherwise state will be null and you will get an error like mentioned in your post.
You have to initialize your state. There are two ways to do that
The first is to use a property initializer
class AddOption extends React.Component<IAddOptionProps, IAddOptionState> {
this.state = {
error: ''
};
render() {
// Now you have your state! Do stuff here
}
}
The other way is to do it in the constructor
class AddOption extends React.Component<IAddOptionProps, IAddOptionState> {
constructor(props) {
super(props);
this.state = {
error: ''
};
}
render() {
// Now you have your state! Do stuff here
}
}
The solutions are equivalent, though the first is more elegant

Cannot call a method in the class

Getting started with React and Redux and I have a method in my class to create new lines in a piece of text:
class JobPage extends React.Component {
componentWillMount() {
this.props.dispatch(fetchJob(this.props.match.params.slug))
}
getMultiLineText(text) {
text.split('\n').map((item, key) => {
return <span key={key}>{item}<br/></span>
})
}
render() {
const { job } = this.props;
return(
{this.getMultiLineText(job.desc)}
)
}
}
Within my render() method I try and call it using the following but the param is not passed. It's null:
{this.getMultiLineText(job.desc)}
If I just do...
{job.desc}
it prints the description.
The value is probably null during the initial render.
You can solve it for example as
{this.getMultiLineText(job.desc || '')}
You can also fix it in your reducer.

Can't access properties of an object

I can't access the keys of the object that I pass through props to my EditField component. When I log the prop I passed into the console it outputs the object correctly, but when I try to get any key of that object I get an "undefined error". What's up with that object? On the picture is the object I get when doing console.log(department) Look at my code:
class EditForm extends React.Component {
render() {
const departments = this.props.departments
const showcasedDepName = this.props.showcasedDepName
const dep = departments.filter(dep => dep.depName ===
showcasedDepName)[0]
return(
...irrelevant stuff
<EditField department={dep}/>
</form>
</div>
)
}
}
EditField component:
class EditField extends React.Component {
render() {
const department = this.props.department
console.log(department.depName) //"undefined", whereas it shows me the correct object when I do console.log(department)
return(
<div className="edit-dep">
<div>Department name: <input type="text" value=
{department.depName}/></div>
</div>
)
}
}
I fixed it! The problem came from where I didn't expect it to come. I counted the amount of workers in componentDidMount function where I changed the state of 'depAmount' key from null to the length of the array of workers. When I replaced componentDidMount with componentWillMount the problem dissappeared. I guess the reason why I had this error is that the state initially rendered with null value.

Get the object clicked from the child component in React

I'd like to trigger the function handleDisplayProduct on click and pass to it the object clicked. So far it calls the function handleDisplayProduct when the list is generated for all the objects but the function is not triggered on the click event.
So how do i bind the event onclick with the Container and passing to it the element clicked?
Container
// Method to retrieve state from Stores
function getAllProductState(){
return {
products: ProductStore.getProducts(),
};
}
export default class ProductAllContainer extends Component {
constructor(props){
super(props);
this.state = getAllProductState();
}
handleDisplayProduct(data){
console.log(data);
// ProductActions.selectProduct(data)
}
render(){
const products = this.state.products;
return(
<div>
{ products.map(function(product,i){
return (
<ProductThumbnail
product = { product }
key = { i }
**onDisplayProduct = { this.handleDisplayProduct(product) }**
/>
)
},this)}
</div>
)
}
}
View
const ProductThumbnail = (props)=>{
return(
<div>
<LinksTo to="/" **onClick={props.onDisplayProduct}**>
<h1>{props.product.headline}</h1>
<img src={props.product.images[0].imagesUrls.entry[1].url} alt="Thumbnail small pic"/>
</LinksTo>
</div>
)
}
You need to bind the event listener to the react class. You can do it this way.
constructor(props){
super(props);
this.state = getAllProductState();
this.handleDisplayProduct = this.handleDisplayProduct.bind(this);
}
or alternatively using es6, you can use an arrow function instead.
handleDisplayProduct = (data) => {
console.log(data);
// ProductActions.selectProduct(data)
}
Note: Class properties are not yet part of current JavaScript standard. So the second example wouldn't work unless you add a babel-plugin-transform-class-properties babel plugin
Edit: Also #ryanjduffy pointed out a crucial mistake in your code. Refer his comment.

Resources