Passing Objects to Component in React - javascript

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

Related

How to append a dynamic HTML element to React component using JSX?

I'm new to Reactjs. I'm creating an App with a Survey creation like Google Forms. My component has a button to create a new Div with some HTML elements to create a survey question. To do that, on the button click function, I'm creating a JSX element and push it to an array. And then, I set the array inside the render function to display what inside it.
The problem is, Even though the Array is updating, the dynamic HTML part can not be seen on the page. The page is just empty except the button. How can I fix this?
Component:
import '../../styles/css/surveycreation.css';
import React, { Component } from 'react';
let questionId = 0;
class SurveyCreation extends Component {
constructor(props) {
super(props);
this.questionsList = [];
this.state = {
}
}
addQuestion = (e) => {
e.preventDefault();
questionId = questionId + 1;
this.questionsList.push(
<div key={questionId}>
question block
</div>
)
}
render() {
return (
<div>
<button onClick={e => this.addQuestion(e)}>Add Question</button>
<div>
{this.questionsList}
</div>
</div>
);
}
};
export default SurveyCreation;
The only way a react component knows to rerender is by setting state. So you should have an array in your state for the questions, and then render based on that array. In the array you want to keep data, not JSX elements. The elements get created when rendering.
constructor(props) {
super(props);
this.state = {
questions: [],
}
}
addQuestion = () => {
setState(prev => ({
// add some object that has the info needed for rendernig a question.
// Don't add jsx to the array
questions: [...prev.questions, { questionId: prev.questions.length }];
})
}
render() {
return (
<div>
<button onClick={e => this.addQuestion(e)}>Add Question</button>
<div>
{this.state.questions.map(question => (
<div key={question.questionId}>
</div>
)}
</div>
</div>
);
}
I think you're component is not re-rendering after you fill the array of elements.
Try adding the questionsList to the component's state and modify your addQuestion method so that it creates a new array, finally call setState with the new array.
You need to map your this.questionsList variable.
You can save the 'question string' in the array and then iterate the array printing your div..
Something like this.
<div>
{this.state.questionsList.map(questionString, i => (
<div key={i}>
{questionString}
</div>
)}
</div>

Parent State not updating when passed data from child component

I'm trying to create a note-taking application in React.
The application should add a new note when an "Add note" button is pressed with the value in the input box.
Unfortunately when I try to push the note to the list and update the parents state the changes aren't reflected on screen or in the react debugger.
The pushing of new note to list can be seen in the alert line but not anywhere else.
Here is the parent component containing the original notes state:
class NoteApplication extends React.Component {
constructor(props) {
super(props);
this.state = {
notes: Array(),
};
this.update = this.update.bind(this);
this.state.notes.push("Sample note");
}
update(notes) {
return () => {
this.setState({
notes: notes
});
}
}
render() {
return (
<div>
<h1>React Notes</h1>
<div class="InsertBarDiv">
<InsertBar
notes={this.state.notes}
update = {this.update}
/>
</div>
<div class="NotesDiv">
<Notes
notes={this.state.notes}
/>
</div>
</div>
)
}
}
And here is the child component
class InsertBar extends React.Component {
constructor(props) {
super(props);
this.state = {value:''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
const notes = this.props.notes.slice();
notes.push(this.state.value);
this.props.update(notes);
alert(notes);
event.preventDefault();
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input class="noteInsertBar" type="text" name="" onChange={this.handleChange}/>
<input class="insertBut" type="submit" value="Add Note"/>
</form>
</div>
)
}
}
class Notes extends React.Component {
renderNote(i) {
return (
<div>
{this.props.notes}
</div>
)
}
render() {
return (
<div>
<h2>Notes:</h2>
<div class="FullNote">
{this.renderNote(1)}
</div>
</div>
)
}
}
I would expect the note to be pushed to the copy of the notes list & the parents state to be set to the new copy of the notes list.
I would then expect this to be displayed onscreen.
It's likely due to the fact that you're returning a function from update, you should just call setState when update gets called:
update(notes) {
setState({ notes });
}
Side note: You should avoid Array.push when dealing with arrays in React. The way you're doing it is fine because you're calling slice to copy the array before you push, but if you use concat or the spread operator, you'll be less likely to unintentionally introduce bugs.
const notes = this.props.notes.concat(this.state.value);
or:
const notes = [...this.props.notes, this.state.value];
I got some help in the react discord thanks to the user #WasaWasaWassup so I'd like to share what fixed my issue.
Mutating the parent state in the constructor to add a sample note was causing issues.
The second issue was my update function returning a function yet being called as if it wasn't.
Removing the constructor mutating & altering my update function to just set the state without an embedded function fixed all my issues and the notes array updates and displays correctly.

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.

React - prop is undefined when loading the same component

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.

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...

Categories

Resources