React - prop is undefined when loading the same component - javascript

I am facing an issue with a React component which I want to use a certain prop in one case and another prop in a different case. Let me show you what I mean.
class GizmoComponent extends React.Component {
render() {
return (
{
this.props.SomeBoolean
?
<WidgetColumn {...this.props} field1={this.props.field2}/>
:
<WidgetColumn {...this.props} field1={this.props.field1}/> {/* field1 is already in the props but I'm being explicit */}
}
);
}
}
class WidgetColumn extends React.Component {
render() {
return (
{
this.props.field1.subfield
?
<div>{/* Extensive use of this.props.field1 throughout this component*/}</div>
:
<div></div>
}
);
}
}
Basically, what I am trying to do is that because WidgetColumn makes extensive use of the this.props.field1, I want to replace the getting of that data with a field2. Everything else remains the same. Just get the data from a different item in a certain case: SomeBoolean.
However, I am getting an error on the this.props.field1.subfield saying that this.props.field1 is undefined so I can't get the subfield of something that's undefined. This only occurs when I add the <WidgetColumn {...this.props} field1={this.props.field2}/> line to the code.
Why is it undefined since I am defining what it is in the prop?

At first, make sure that SomeBoolean and field1.subfield/field2.subfield properties are passing properly.
My recomendation is: try not to spread props object {...this.props} when passing parameters to the WidgetColumn.
As I understood GizmoComponent has field1 and field2 props:
GizmoComponent.propTypes = {
field1: PropTypes.object
field2: PropTypes.object
}
So when you will spread GizmoComponent props into another component like:
// NOTE: there are this.props.field1 and this.props.field2 are available
<WidgetColumn {...this.props} />
The result will be the same as you will write:
<WidgetColumn field1={this.props.field1} field2={this.props.field2} />
It's possible that you have conflict and spread object rewrites the value of the props that you defined manually.
Try to pass field property on next way:
class WidgetColumn extends React.Component {
render() {
return this.props.field.subfield
? <div>The field is subfield</div>
: <div>The field is NOT subfield</div>
}
}
class GizmoComponent extends React.Component {
render() {
return this.props.SomeBoolean
? <WidgetColumn field={this.props.field2} />
: <WidgetColumn field={this.props.field1} />
}
}
class Example extends React.Component {
render() {
return (
<p>
<GizmoComponent field1={{ subfield: true }} field2={{ subfield: false }} SomeBoolean={true} />
<GizmoComponent field1={{ subfield: true }} field2={{ subfield: false }} SomeBoolean={false} />
</p>
);
}
}
ReactDOM.render(
<Example />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

You have to declare property while Rendering Component.
Like
<WidgetColumn props={this.props} field1={this.props.field2}/>
If you use this then you can access all props of parent component in child component.
That's it.

Related

Passing Objects to Component in React

I'm fairly new to the react side of life and have come across a slightly annoying issue with my Syntax that I could do with a little help on.
The premise is very simple:
I am trying to pass an object as a prop to a Component.
Parent Element: -- Trying to pass the state to the Totals component
class Dash_overview extends React.Component{
constructor(props){
super(props)
this.state = {
companies: {
title: 'companies on record',
value: null,
messurement: 'Companies'
},
earning: {
title: 'total earning',
value: null,
messurement: 'mill'
}
}
}
render(){
return (
<div className="overview-container">
<div className="totals">
<Totals values={this.state.companies}/>
<Totals values={this.state.earning}/>
</div>
</div>
)
}
}
Child Component -- which is going to use the values passed to it
class Totals extends React.Component{
constructor(props){
super(props)
this.state = {
}
}
render(){
return (
<div className="totals_comp">
<h3>{companies.title}</h3>
<h3>{companies.value}</h3>
<h3>{companies.messurement}</h3>
</div>
)
}
}
--
Im probably making a silly mistake but I have tried a few different variations of this without success so would really value someone pointing out where I am going wrong. :)
Thanks in advance,
Wally
You can spread the state values into the child component props, the object keys will be the prop names used within the component.
<Totals {...this.state.companies}/>
<Totals {...this.state.earning}/>
Or explicitly pass the prop values
const { messurement, title, value } = this.state.companies;
...
<Totals
messurement={messurement}
title={title}
value={value}
/>
<Totals
messurement={messurement}
title={title}
value={value}
/>
And then in the child access via props
<div className="totals_comp">
<h3>{this.props.title}</h3>
<h3>{this.props.value}</h3>
<h3>{this.props.messurement}</h3>
</div>
Issue
values={this.state.companies} takes the state object value and assigns it to a prop named values, but then in the child component you don't reference it at all. i.e. like props.values.title.
Since you are passing {title: 'companies on record',value: null,messurement: 'Companies'} as values prop, you should consume values from the other component. if you want to use companies name do this :
<div className="overview-container">
<div className="totals">
<Totals companies={this.state.companies}/>
<Totals companies={this.state.earning}/>
</div>
</div>
and then do this on Totals component:
const {companies}=this.props
render(){
return (
<div className="totals_comp">
<h3>{companies.title}</h3>
<h3>{companies.value}</h3>
<h3>{companies.messurement}</h3>
</div>
)}
Try this.
const { title,value,messurement } = this.props.values;
render(){
return (
<div className="totals_comp">
<h3>{title}</h3>
<h3>{value}</h3>
<h3>{messurement}</h3>
</div>
)
}

Changing the state in reactJs parent is not changing the props in react child components

I have a Parent React Component and which have 3 Child components i am changing the state in parent component but changing the state in parent is not changing the props in child components.I am passing the state from parent to child components but the props are not changing inside the child components.
My parent component
class Parent extends Component {
state = {
menuCategoryId:'',
}
handelOnClickRefundMenu = () => {
this.setState({menuCategoryId:''});
}
render() {
return (
<FoodMenu
menuCategories={menuCategories}
{...this.state}
/>
)
}
}
export default Parent;
Child 1 Component
class FoodMenu extends Component {
render() {
return (
<MenuCategories
MenuCategories={menuCategories.MenuCategories}
selectedMenuCategoryId={this.props.menuCategoryId}
/>
);
}
}
export default Child1;
Child 2 component
class MenuCategories extends React.Component{
render(){
const MenuCategories = this.props.MenuCategories;
const selectedMenuCategoryId = this.props.selectedMenuCategoryId;
const renderCategories = (MenuCategories) => (
MenuCategories ?
MenuCategories.map(card=>(
<MenuCategory
key={card._id}
{...card}
handleOnClickMenuCategory={this.props.handleOnClickMenuCategory}
selectedMenuCategoryId={this.props.selectedMenuCategoryId}
// propData={...this.props}
/>
))
:null
)
return (
<Fragment>
<div id="" className="food-menus-menu w-100">
<div className="row">
<OwlCarousel
className="owl-theme"
loop={true}
items={9}
autoplay={false}
autoplayTimeout={3000}
autoplayHoverPause={false}
nav={true}
navElement={'div'}
navText={[`<img src=${seventheenPng}>`,`<img src=${eitheenPng}>`]}
dots={false}
responsive={{
0:{
items:4
},
600:{
items:3
},
1000:{
items:7
}
}}
>
{MenuCategories ?
MenuCategories.length === 0 ?
<div className="no_result">
Sorry, no results
</div>
:null
:null}
{ renderCategories(MenuCategories)}
</OwlCarousel>
</div>
</div>
</Fragment>
)
}
};
export default MenuCategories;
Child 3 Component
class MenuCategory extends Component {
render() {
const props = this.props;
console.log('The values of the props are not changing here')
console.log(props.selectedMenuCategoryId)
return (
<div className={`colCategory item`} onClick={()=>props.handleOnClickMenuCategory(props)}>
<button
className={`btn btn-primary w-100 py-2 d-inline-flex align-items-center justify-content-center ${props.selectedMenuCategoryId === props._id ? 'activeMenuCategory' : ''}`}>
{props.name}
</button>
</div>
);
}
}
export default MenuCategory;
The value of props "props.selectedMenuCategoryId" in my last component which is inside the Map function MenuCategory is not changing when i change the state in my Parent Class function handelOnClickRefundMenu
The Map function is inside Child Component 2 MenuCategories .
Kindly help me on this please.
Thanks in advance.
All the answers about forcing re-renders using lifecycle methods are wrong. If you pass the props down correctly and they change, your child components should re-render automatically.
To demonstrate this, here's a quick'n'dirty sandbox that has a parent and two children that passes a prop down as you require.
I don't know exactly what's wrong with your code (a self-contained example that we can run and debug would help here), but I suggest paring it back to a simpler case that you can get working and then build up from there.
eta: Are you sure the problem isn't to do with your click handler? You're not passing it in to FoodMenu or MenuCategories.
Why are you naming your prop and variables the same as your component? It’s really hard to read and is probably causing confusion as to whether you’re referencing the component or the prop/variable. Use camel case
Naming your prop MenuCategories inside the component MenuCategories is not only bad practice, but could solve the issue if named menuCategories instead.

Passing props/state to/from parent component

So I have a parent component and a log in component.
I want the user to enter their details and then hit submit and then store/pass those details around so they can be used by other components.
how is this best done in React?
for example I have this input field inside my log in component
<p>
<input type="text" id="playerName" value={this.props.nameValue} onChange={this.props.handleNameChange}/>
</p>
Then I want to pass the value that is entered to the parent component
I have this function in my parent component:
handleNameChange(event){
this.setState({nameValue: event.target.value})
};
and in my return I have:
return (
<div>
<LoginPage handleClick={this.handleClick.bind(this)} handleNameChange={this.handleNameChange.bind(this)}/>
</div>
)
However, when I console.log(nameValue) I get undefined. any ideas? can add more code if necessary/relevant
From your example you never pass nameValue to the child component.
Updated your example of rendering the LoginPage, passing this.state.nameValue into the child component via props:
return (
<div>
<LoginPage
handleClick={this.handleClick.bind(this)}
handleNameChange={this.handleNameChange.bind(this)}
nameValue={this.state.nameValue}
/>
</div>
)
Your approach using state and props is fine. Are you sure that you shouldn't just be using...
console.log(this.state.nameValue);
This is a working example
class Parent extends React.Component {
constructor() {
super();
this.state = {
nameValue:''
};
}
render() {
return (
<Child handleClick={this.handleClick.bind(this)} handleNameChange={this.handleNameChange.bind(this)} nameValue={this.state.nameValue} />
);
}
handleNameChange(e) {
this.setState({
nameValue: e.target.value
});
}
handleClick() {
alert(this.state.nameValue);
}
}
class Child extends React.Component {
render() {
return (
<div>
<input type="text" value={this.props.nameValue} onChange={this.props.handleNameChange} />
<button onClick={this.props.handleClick}>Click Me!</button>
</div>
);
}
}
JSFiddle here.

What's the proper way to pass dependencies between components in React?

Imagine that Component A creates a list of items that Component B needs to display. What's the proper way to pass data from Component A to Component B from their parent?
For example, let's say that Component A's constructor creates a list of items and has a function _getListItems() that returns that list. I'm hoping the parent can then pass that list on to other components via props.
My naive (non-working) implementation has their parent attempting to render the components like this:
render () {
return (
<div>
<h1>Data Test</h1>
<ComponentA ref='compa'/>
<ComponentB items={this.refs.compa._getListItems()}/>
</div>
);
}
....although the code above doesn't work, I hope it illustrates what I'm trying to do.
ps. nOOb to react and javascript, so forgive me if the answer to my question's obvious...
Divide your components into two separate categories.
Presentational Component that has responsibility to display a thing. This component should not have state (except for UI state).
Container Component that knows the data.
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.skmxo7vt4
So, in your case the data should created by parent of ComponentA and ComponentB and pass the data to both ComponentA and ComponentB via props.
Example:
render(){
let items = this._getListItems();
return (
<div>
<ComponentA items={items} />
<ComponentB items={items} />
</div>
);
}
Edit
Rewrite OP's approach in the comment:
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.state = { stuff: [1,2,3] };
}
render() {
return (
<div>
<ComponentA items={this.state.stuff} />
<ComponentB items={this.state.stuff} />
</div>
);
}
}
Following the accepted answer above, I've just had a (related) EUREKA moment, so I'm going to expand on the answer; when the parent uses its own state to pass props to its children, whenever the parent's state changes, its render() function is called, thus updating the children with the updated state. So you can do stuff like this:
class MyContainer extends React.Component {
constructor(props) {
super(props);
let sltd = this.props.selected
this.state = {
stuff: [1,2,3],
selected: sltd
};
}
_handleAStuff(value) {
this.setState(selected: value)
//do other stuff with selected...
}
_handleBStuff(value) {
this.setState(selected: value)
//do other stuff with selected...
}
render() {
return (
<div>
<ComponentA items={this.state.stuff} selected={this.state.selected} parentFunc={this._handleAStuff.bind(this)} />
<ComponentB items={this.state.stuff} selected={this.state.selected} parentFunc={this._handleBStuff.bind(this)} />
</div>
);
}
}
MyContainer.defaultProps = {
selected: 0
}
class ComponentA extends React.Component {
constructor(props) {
super(props)
}
_handleSelect(value) {
this.props.parentFunc(value.label)
}
render() {
const itm = this.props.items.map(function(values) {
return { value: values, label: values}
})
return (
<div>
<Select
options={itm}
value={this.props.selected}
onChange={this._handleSelect.bind(this)}
/>
</div>
);
}
}
// ComponentB...
The callback pattern above means that ComponentA and ComponentB do not need to maintain state, they simply 'render stuff', which is also pretty cool. I'm beginning to see the power of REACT...

React - Can't use refs in child component when rendering them depending on state

I ran into a weird bug trying to use refs.
Parent component:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {displayPage: 'one'};
this.changePage = this.changePage.bind(this);
}
changePage(str){
this.setState({
displayPage: str
})
}
render(){
return(
<div onClick={ () => this.displayPage('one')}>One</div>
<div onClick={ () => this.displayPage('two')}>Two</div>
<div onClick={ () => this.displayPage('three')}>Three</div>
{this.state.displayPage === 'one' ? <One /> : true}
{this.state.displayPage === 'two' ? <Two /> : true}
{this.state.displayPage === 'three' ? <Three /> : true}
);
}
}
Now, just a simple example of a child component:
class Two extends Component {
render(){
console.log(this, this.refs)
return(
<div refs="test">This is the Two component</div>
);
}
}
My problem is that the console.log for "this" will show a property of "refs" that has everything I want. When it logs "this.refs" all I get back is "Object {}". This will only happen in components Two and Three, or basically, the components that aren't immediately displayed because of the state. "this.refs" will always work for the component immediately displayed.
Also, if I took them out of the ternary condition then refs will work as intended.
change refs in the div to ref like such:
ref="test"
also, just assigning refs by string is getting deprecated, so I suggest you just pass in a callback to a ref that reassigns it as a static property to the class like this:
ref={(node) => { this.test = node; }}

Categories

Resources