I’ve been tasked with building out a project for a client that entails one parent component wrapping three sub components. Specifically the parent component renders the sub components into being according to the following manner:
PARENT = TOP + MIDDLE + BOTTOM
The PARENT component represents one of a group of items.
In the TOP component there is a menu button that displays a list of all items in the group with the intent that should the user click on one of those links, the selected item will display (PARENT will “re-render to” the new item – there’s logic in PARENT that will cause the selected, second, item to appear rather than the first).
I’m speaking loosely here and can supply code if what I’m talking about it isn’t clear (I think it should be).
The primary issue I’m having is trying to “re-render” the parent component from one of the child components. Essentially, there’s a menu button that appears displaying a list of related items. Clicking on one of those items should re-render the parent component but this time displaying the selected item. I don’t know of a way to do this however and was hoping to get some advice or help. I’ve spent the morning researching the topic and trying a few suggested ways to no avail.
Perhaps the approach I’ve taken so far is not the best to accomplish this sort of task. In any event, any assistance would be very helpful. Thank you!
EDIT: NOW WORKING
EDIT #2: Since this has gotten quite a few views I just wanted to clarify that this was a proof of concept and also very simplified prototype/early version (no fancy transpilation, etc. – a can this idea work sort of thing … since we weren’t sure at the time). It’s been a while since I worked on anything like that but I really appreciate all the help for such a confusing question that I asked and what was at that time a very interesting and challenging task.
import React from 'react';
import CardHeader from 'components/CardHeader';
import CardContent from 'components/CardContent';
import CardFooter from 'components/CardFooter';
module.exports = React.createClass({
getInitialState: function () {
return {
fullData: '',
//Core Card
userId: '',
cardStack: '',
cardId: '',
//Load Card
loadCard: '1',
//Optional Fields
name: '',
type: '',
headline: '',
subtitle: '',
ctext: '',
imageUrl: '',
price: '',
mapCoordinates: '',
logoUrl: '',
order: '',
email: '',
sponsorUrl: '',
pollId: '',
socialButton: ''
};
}
,
componentWillMount: function () {
//fetches cardStack and card API JSON
/** JSON Structure:
[
cardStack: ...,
cards: ...
]
**/
var fetch = function (userId, cardStackId) {
//AJAX
var json = {
'cardStack': {'id': '1', 'name': 'Test Card Stack', 'userID': 'O1AB0001'},
'cards': [{
'id': '1',
'name': 'Test Card 1',
'cardStack': '1',
'meta': 'meta_note',
'socialButton': 'twitter',
'sponsorUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
'order': 1,
'logoUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
'product': {
'headline': 'Headline Test',
'subtitle': 'Subtitle Test',
'ctext': 'Hello!!!!',
'imageUrl': 'http://the-mpas.com/wp-content/uploads/2012/04/Image-pic-54-copy.jpg',
'price': '20.00'
}
}, {
'id': '2',
'name': 'Test Card 2',
'cardStack': '1',
'meta': 'meta_note',
'socialButton': 'twitter',
'sponsorUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
'order': 2,
'logoUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
'product': {
'headline': 'Headline Test 2',
'subtitle': 'Subtitle Test 2',
'ctext': 'Hello 2!!!!',
'imageUrl': 'http://the-mpas.com/wp-content/uploads/2012/04/Image-pic-54-copy.jpg',
'price': '30.00'
}
}]
};
return json;
};
var that = this;
var getCard = function (cardArray, cardOrder) {
var card;
for (var key in cardArray) {
if (cardArray[key].order == cardOrder) {
card = cardArray[key];
}
}
if ('product' in card) {
that.setState({
type: 'product',
headline: card.product.headline,
subtitle: card.product.subtitle,
ctext: card.product.ctext,
imageUrl: card.product.imageUrl,
price: card.product.price
})
}
if ('article' in card) {
that.setState({
type: 'article',
headline: card.article.headline,
ctext: card.article.ctext
})
}
if ('map' in card) {
that.setState({
type: 'map',
mapCoordinates: card.map.mapCoordinates
})
}
if ('relatedvideo' in card) {
that.setState({
type: 'relatedvideo',
headline: card.relatedvideo.headline,
ctext: card.relatedvideo.ctext,
imageUrl: card.relatedvideo.imageUrl
})
}
if ('poll' in card) {
that.setState({
type: 'poll',
headline: card.poll.headline,
subtitle: card.poll.subtitle,
pollId: card.poll.pollId
})
}
if ('imagegallery' in card) {
that.setState({
type: 'imagegallery',
headline: card.imagegallery.headline,
ctext: card.imagegallery.ctext,
imageUrl: card.imagegallery.imageUrl
})
}
if ('profile' in card) {
that.setState({
type: 'profile',
headline: card.profile.headline,
ctext: card.profile.ctext,
imageUrl: card.profile.imageUrl
})
}
if ('newsletter' in card) {
that.setState({
type: 'newsletter',
email: card.newsletter.email
})
}
return card;
};
//Entry Point HERE
var userId = 'O1AB0001', cardStackId = 1;
var json = fetch(userId, cardStackId);
var myCard = getCard(json.cards, this.state.loadCard);
//Set core data
this.setState({
//fulldata
fullData: json,
//card stack
userId: json.cardStack.name,
cardStack: json.cardStack.id,
//card
cardId: myCard.id,
socialButton: myCard.socialButton,
order: myCard.order,
sponsorUrl: myCard.sponsorUrl,
logoUrl: myCard.logoUrl,
meta: myCard.meta,
name: myCard.name
});
},
setNew: function (nextState) {
var nsFullData = nextState.fullData;
var nsCards = nsFullData.cards;
var nsCardStack = nsFullData.cardStack;
var that = this;
var getCard = function (cardArray, cardOrder) {
var card;
for (var key in cardArray) {
if (cardArray[key].order == cardOrder) {
card = cardArray[key];
}
}
if ('product' in card) {
that.setState({
type: 'product',
headline: card.product.headline,
subtitle: card.product.subtitle,
ctext: card.product.ctext,
imageUrl: card.product.imageUrl,
price: card.product.price
})
}
if ('article' in card) {
that.setState({
type: 'article',
headline: card.article.headline,
ctext: card.article.ctext
})
}
if ('map' in card) {
that.setState({
type: 'map',
mapCoordinates: card.map.mapCoordinates
})
}
if ('relatedvideo' in card) {
that.setState({
type: 'relatedvideo',
headline: card.relatedvideo.headline,
ctext: card.relatedvideo.ctext,
imageUrl: card.relatedvideo.imageUrl
})
}
if ('poll' in card) {
that.setState({
type: 'poll',
headline: card.poll.headline,
subtitle: card.poll.subtitle,
pollId: card.poll.pollId
})
}
if ('imagegallery' in card) {
that.setState({
type: 'imagegallery',
headline: card.imagegallery.headline,
ctext: card.imagegallery.ctext,
imageUrl: card.imagegallery.imageUrl
})
}
if ('profile' in card) {
that.setState({
type: 'profile',
headline: card.profile.headline,
ctext: card.profile.ctext,
imageUrl: card.profile.imageUrl
})
}
if ('newsletter' in card) {
that.setState({
type: 'newsletter',
email: card.newsletter.email
})
}
return card;
};
var myCard = getCard(nsCards, this.state.loadCard);
this.setState({
//fulldata
fullData: nsFullData,
//card stack
userId: nsCardStack.name,
cardStack: nsCardStack.id,
//card
cardId: myCard.id,
socialButton: myCard.socialButton,
order: myCard.order,
sponsorUrl: myCard.sponsorUrl,
logoUrl: myCard.logoUrl,
meta: myCard.meta,
name: myCard.name
});
},
componentWillUpdate: function (nextProps, nextState) {
if (nextState.loadCard !== this.state.loadCard) {
this.setNew(nextState);
}
},
render: function () {
return (
<div className='sg-cardBase'>
<div className='sg-cardHeaderSection'>
<CardHeader setLoadCard={i => this.setState({loadCard: i})} data={this.state}/>
</div>
<div className='sg-cardContentSection'>
<CardContent data={this.state}/>
</div>
<div className='sg-cardFooterSection'>
<CardFooter data={this.state}/>
</div>
</div>
);
}
});
2
Answers
You need to pass a callback into the child component that modifies the state of the parent component. The parent can then re-render based on that state. For example:
In your case, the currently selected item should be stored in the state of your parent component, and a callback passed into your top component so that it can change the selected item.
I assume that the top and bottom stays, and the middle changes according to the menu.
The top gets a callback from the parent. When an option is selected in the top, it invokes the callback
changeCurrentView
, and notifies it about thecurrentView
. The callbackchangeCurrentView
sets the parent state, invoking the render method.You can now change the render middle in the parent, but I suggest that the middle will re-render the different views. So middle gets the
currentView
as view from the parent, and it re-renders the desired view: