How to track state of parent component from child component in ReactJs? - javascript

I have a page with photoalbums, and I need edit each album by click on the edit button in album component. I can't understand how to communicate between components if I have no common store(redux for example)
How to track state of parent component from child component - Modal?
var AlbumsPage = React.createClass({
render: function ()
{
return(
<div>
<AlbumList url="Album/List"/>
</div>
);
}
});
var AlbumList = React.createClass({
componentDidMount: function ()
{
$.ajax({
url: this.props.url,
dataType: 'json',
method: "POST",
cache: false,
success: function (data) {
this.setState({ data: data.Albums });
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function()
{
return this.getAlbumListState();
},
getAlbumListState: function()
{
return {
data: [],
currentAlbum: null,
showModal: false
}
},
setCurrentAlbum: function(album){
this.state.currentAlbum = album;
},
setShowModal : function(val){
this.state.showModal = val;
},
getShowModal: function(){
return this.state.showModal;
},
render: function () {
var albums = this.state.data.map(function(album) {
return (
<Album key={ album.Id } title={ album.Title } getShowModal={ this.getShowModal } setCurrentAlbum={ this.setCurrentAlbum } setShowModal={ this.setShowModal }></Album>
);
}, this);
return (
<div>
<div>
{ albums }
</div>
<AlbumModal showModal={ this.state.showModal } currentAlbum={ this.state.currentAlbum }/>
</div>
);
}
});
var Album = React.createClass({
open : function()
{
console.log("open fired");
console.log(this.props.getShowModal());
if (this.props.getShowModal()) {
this.props.setShowModal(false);
this.props.setCurrentAlbum(null);
} else {
this.props.setShowModal(true);
this.props.setCurrentAlbum(this.props.album);
}
},
render: function () {
return (
<div className="col-sm-3 col-md-3">
<div className="thumbnail">
<div className="caption">
<h3>{ this.props.title }</h3>
<p>
<a onClick={ this.open }><span className="glyphicon glyphicon-pencil"></span></a>
<a><span className="glyphicon glyphicon-trash"></span></a>
</p>
</div>
</div>
</div>
);
}
});
var AlbumModal = React.createClass({
getInitialState: function() {
return {
showModal: this.props.showModal,
currentAlbum: this.props.currentAlbum
};
},
close: function() {
this.setState({ showModal: false });
},
open: function() {
this.setState({ showModal: true });
},
render: function() {
return (
<div>
<ReactBootstrap.Modal show={this.state.showModal} onHide={this.close}>
<ReactBootstrap.Modal.Header closeButton>
<ReactBootstrap.Modal.Title>Modal heading</ReactBootstrap.Modal.Title>
</ReactBootstrap.Modal.Header>
<ReactBootstrap.Modal.Body>
<h4>Text in a modal</h4>
</ReactBootstrap.Modal.Body>
<ReactBootstrap.Modal.Footer>
<ReactBootstrap.Button onClick={this.close}>Close</ReactBootstrap.Button>
</ReactBootstrap.Modal.Footer>
</ReactBootstrap.Modal>
</div>
)
}
});
ReactDOM.render(
<AlbumsPage />,
document.getElementById('albums')
);

You'd have to pass the parent as a prop to the child component.
So in your AlbumList::render, you'd have to do
return (
<Album
key={ album.Id }
title={ album.Title }
getShowModal={ this.getShowModal }
setCurrentAlbum={ this.setCurrentAlbum }
setShowModal={ this.setShowModal }
parent={this}
>
</Album>
);
But this will create a huge overhead once you start passing states to many different components.
A good plugin to solve this would be to use Redux

Related

ReactJS - moving list items between components

I am trying to create two components, one that holds the results of an API call from iTunes. I want to be able to click on any one of the items and move it to the empty component, moveResults, and then move it back to searchResults if it is clicked again. From other exercises, I feel like I am close, however I keep getting an error about the this.handleEvent = this.handleEvent.bind(this). Any ideas as to where I might have gone wrong and some possible solutions?
var App = React.createClass({
getInitialState: function() {
return {
searchResults: [],
moveResults: []
}
},
this.handleEvent = this.handleEvent.bind(this);
showResults: function(response) {
this.setState({
searchResults: response.results,
moveResults: []
})
},
search: function(URL) {
$.ajax({
type: 'GET',
dataType: 'json',
url: URL,
success: function(response) {
this.showResults(response);
}.bind(this)
});
},
handleEvent(trackId) {
const isInSearchResults = this.state.searchResults.includes(trackId);
this.setState({
searchResults: isInSearchResults ? this.state.searchResults.filter(i => i !== trackId) : [...this.state.searchResults, trackId],
moveResults: isInSearchResults ? [...this.state.moveResults, trackId] : this.state.moveResults.filter(i => i !== trackId)
});
},
componentDidMount() {
this.search('https://itunes.apple.com/search?term=broods')
},
render: function(){
return (
<div>
<Results searchResults={this.state.searchResults} handleEvent={this.handleEvent}/>
<Results searchResults={this.state.moveResults} handleEvent={this.handleEvent} />
</div>
);
}
});
var Results = React.createClass({
render: function(){
let handleEvent = this.props.handleEvent;
var resultItems = this.props.searchResults.map(function(result) {
return <ResultItem key={result.trackId} trackName={result.trackName} onClick={() => handleEvent(resultItems.id)} />
});
return(
<ul>
{resultItems}
</ul>
);
}
});
var ResultItem = React.createClass({
render: function(){
return <li> {this.props.trackName} </li>;
}
});
ReactDOM.render(
<App />, document.getElementById('root')
);

Unable to render json data in ReactJS components

I am trying to show data from package.json in my ReactJS components. I have seen many answers on SO and tried to implement it. It is neither showing any data nor an error.
This is package.json:
{
"name":"Clark Kent",
"job":"Superman"
}
This is my jsx:
var App = React.createClass({
getInitialState: function(){
return { myData: [] };
},
showResults: function(response){
this.setState({
myData: response
});
},
loadData: function(URL) {
var that = this;
$.ajax({
type: 'GET',
dataType: 'jsonp',
url: URL,
success: function(response){
that.showResults(response);
}
})
},
componentDidMount: function() {
this.loadData("package.json");
},
render: function(){
return(
<div>
<Component1 data={this.state.myData} />
<Component2 data={this.state.myData}/>
</div>
)
}
});
var Component1 = React.createClass({
render: function () {
return(
<div>Name: {this.props.data.name}</div>
)
}
});
var Component2 = React.createClass({
render: function () {
return(
<div>Job: {this.props.data.job}</div>
)
}
});
ReactDOM.render(<App/>, document.getElementById('container'));

React component not making ajax call

I am trying to implement a component which handles ajax requests to the server and passes data to two child components. However it appears the ajax calls are never being made.
Feed.js:237 Uncaught TypeError: Cannot read property 'data' of null
is the error I'm getting.
// Parent component containting feed and friends components,
// handles retrieving data for both
import React, { Component } from 'react'
import FeedBox from './Feed.js'
import FriendsBox from './Friends'
const config = require('../../config')
export default class FrontPage extends Component {
constructor(props) {
super(props)
this.state = {
data: {},
pollInterval: config.pollInterval
}
this.loadDataFromServer = this.loadDataFromServer.bind(this)
}
loadDataFromServer() {
$ajax({
url: config.apiUrl + 'frontpage',
dataType: 'jsonp',
cache: false,
success: (data) => {
this.setState({data: data})
console.log(data)
},
error: (xhr, status, err) => {
console.error(this.url, status, error.toString())
}
})
}
componentDidMount() {
this.loadDataFromServer()
setInterval(this.loadDataFromServer, this.state.pollInterval)
}
componentWillUnmount() {
this.state.pollInterval = false
}
render() {
return (
<div className="FrontPage">
<FeedBox data={ this.state.data.feedData } />
<FriendsBox data={ this.state.data.friendsData } />
</div>
)
}
}
EDIT:
Here is the code for the Feed component, one of the two child components of FrontPage which is being passed props from its parent
import React, { Component } from 'react';
var config = require('../../config')
class Thread extends Component {
rawMarkup() {
var rawMarkup = marked(this.props.children.toString(), { sanitize: true })
return {__html: rawMarkup }
}
render() {
return (
<div className="thread">
<h4 className="threadVictim">Dear {this.props.victim}: </h4>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
<p>signed,</p>
<div>{this.props.author} and {this.props.ct} others.</div>
<hr></hr>
</div>
)
}
}
class ThreadList extends Component {
render() {
var threadNodes = this.props.data.map(function (thread) {
return (
<Thread victim={ thread.victim } author={ thread.author } ct={ thread.included.length } key={ thread._id }>
{ thread.text }
</Thread>
)
})
return (
<div className="threadList">
{ threadNodes }
</div>
)
}
}
var ThreadForm = React.createClass({
getInitialState: function () {
return {author: '',
text: '',
included: '',
victim: '',
ct: ''
}
},
handleAuthorChange: function (e) {
this.setState({author: e.target.value})
},
handleTextChange: function (e) {
this.setState({text: e.target.value})
},
handleIncludedChange: function (e) {
this.setState({included: e.target.value})
},
handleVictimChange: function (e) {
this.setState({victim: e.target.value})
},
handleSubmit: function (e) {
e.preventDefault()
var author = this.state.author.trim()
var text = this.state.text.trim()
var included = this.state.included.trim()
var victim = this.state.victim.trim()
if (!text || !author || !included || !victim) {
return
}
this.props.onThreadSubmit({author: author,
text: text,
included: included,
victim: victim
})
this.setState({author: '',
text: '',
included: '',
victim: '',
})
},
render: function () {
return (
<form className="threadForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your name"
value={this.state.author}
onChange={this.handleAuthorChange} />
<input
type="text"
placeholder="Say something..."
value={this.state.text}
onChange={this.handleTextChange} />
<input
type="text"
placeholder="Name your victim"
value={this.state.victim}
onChange={this.handleVictimChange} />
<input
type="text"
placeholder="Who can see?"
value={this.state.included}
onChange={this.handleIncludedChange} />
<input type="submit" value="Post" />
</form>
)
}
})
var ThreadsBox = React.createClass({
// loadThreadsFromServer: function () {
// $.ajax({
// url: config.apiUrl + 'feed',
// dataType: 'jsonp',
// cache: false,
// success: function (data) {
// this.setState({data: data})
// }.bind(this),
// error: function (xhr, status, err) {
// console.error(this.url, status, err.toString())
// }.bind(this)
// })
// },
handleThreadSubmit: function (thread) {
var threads = this.state.data
var newThreads = threads.concat([thread])
this.setState({data: newThreads})
$.ajax({
url: config.apiUrl + 'threads',
dataType: 'json',
type: 'POST',
data: thread,
success: function (data) {
this.setState({data: data})
}.bind(this),
error: function (xhr, status, err) {
this.setState({data: threads})
console.error(this.url, status, err.toString())
}.bind(this)
})
},
// getInitialState: function () {
// return {data: [],
// pollInterval: config.pollInterval}
// },
// componentDidMount: function () {
// this.loadThreadsFromServer()
// setInterval(this.loadThreadsFromServer, this.state.pollInterval)
// },
// componentWillUnmount: function () {
// this.state.pollInterval = false;
// },
render: function () {
return (
<div className="threadsBox">
<div className="feedNav">
<h1>Home</h1>
<h1>Heat</h1>
</div>
<ThreadList data={ this.state.data } />
<ThreadForm onThreadSubmit={ this.handleThreadSubmit } />
</div>
)
}
})
module.exports = ThreadsBox
You need to define the initial state object in ThreadsBox so it is not null:
var ThreadsBox = React.createClass({
getInitialState: function() {
return {
data: {}
};
}
// ...
})
module.exports = ThreadsBox
the ajax call is not firing. try adding the method for the request: GET or POST.

Nesting three components in React

I was trying to pass a function deleteComment() from my parent component to the child component so that when the child component is clicked, it will run deleteComment(). As you can see, I defined deleteComment() in CommentBox and passed it down to the CommentList component and then the Comment component. When I tried pass it down only once to CommentList, the onClick event work and deleteComment ran, but once I pass it down another level, it does not. Am I referring to the function incorrectly while nesting multiple components?
var Comment = React.createClass({
rawMarkup: function() {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return { __html: rawMarkup };
},
render: function() {
return(
<div className="comment" onClick={this.props.onDeleteComment}>
<h3 className="commentTag"><span className="commentTitle">{this.props.title}</span> by <span className="commentAuthor">{this.props.author}</span><i className="fa fa-times"></i></h3>
<div className="commentBody"><span dangerouslySetInnerHTML={this.rawMarkup()}/></div>
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return (
<Comment author={comment.author} key={comment.id} title={comment.title}>
{comment.text}
</Comment>
);
});
return(
<div className="commentList" onDeleteComment={this.props.onDelete}>
{commentNodes}
</div>
)
}
});
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
comment.id = Date.now();
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: comments});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
deleteComment: function() {
alert("test");
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return(
<div className="commentBox">
<ul className="mainBar">
<li className="active">Updates</li>
</ul>
<CommentForm onCommentSubmit={this.handleCommentSubmit}/>
<CommentList data={this.state.data} onDelete={this.deleteComment}/>
</div>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" pollInterval={500} />,
document.getElementById('content')
);
CommentList is rendering
return(
<div className="commentList" onDeleteComment={this.props.onDelete}>
{commentNodes}
</div>
)
You want to pass onDeleteComment to your Comments, not the div wrapping them.
But since it's being .mapd with an anonymous function, scoping changes. Tell it what this should be with .bind
this.props.data.map(function(comment) {
return (
<Comment author={comment.author} key={comment.id} title={comment.title} onDeleteComment={this.props.onDelete}>
{comment.text}
</Comment>
);
}.bind(this))

React DnD in a Meteor app

I am using react with my meteor app and I need to integrate this with a list of items.
There is a ItemList.jsx and a ItemRow.jsx since the item itself has text, links, images, ect.
ItemRow.jsx
ItemRow = ReactMeteor.createClass({
templateName: "ItemRow",
propTypes: {
item: React.PropTypes.object.isRequired,
showPoolTitle: React.PropTypes.bool,
},
getInitialState: function() {
return {
editing: false,
drop: this.props.item,
pool: {},
}
},
startMeteorSubscriptions: function() {
Meteor.subscribe("ItemRow", this.props.item._id);
},
getMeteorState: function() {
return {
item: Items.findOne(this.props.item._id),
pool: Pools.findOne(this.props.drop.poolId),
};
},
toggleEditing: function() {
this.setState({ editing: !this.state.editing });
},
render: function() {
var content;
if (this.state.editing) {
content = <ItemRowEdit drop={this.state.item} done={this.toggleEditing}/>
} else {
content = <ItemRowShow pool={this.state.pool} item={this.state.item} edit={this.toggleEditing} showPoolTitle={this.props.showPoolTitle}/>
}
return (
<div key={this.props.item._id} className="item">
{content}
</div>
)
},
});
and ItemList.jsx
ItemList = ReactMeteor.createClass({
templateName: "ItemList",
propTypes: {
items: React.PropTypes.object.isRequired
},
getInitialState: function() {
return {
items: this.props.items.fetch()
}
},
render: function() {
var items = this.state.items;
return (
<div className="ui very relaxed huge list">
{items.map(function(item) {
return ( <ItemRow item={item}/> );
})}
</div>
);
}
});
I am not using Meteor react for ItemList just yet but I want to be able to drag and drop reorder first so any help to get me on the right track is much appreciated!

Categories