I have three separate files.
Nav.js
var NavItem = React.createClass({
render: function() {
return (
<li>{this.props.name}</li>
);
}
});
var NavList = React.createClass({
render: function() {
var navNodes = this.props.data.map(function(nav) {
return (
<NavItem name={nav.name} key={nav.id}></NavItem>
);
});
return (
<ul className="nav">
<li className="current"><i className="glyphicon glyphicon-home"></i> Lists</li>
{navNodes}
<li><i className="glyphicon glyphicon-plus"></i> Add New Widget List</li>
</ul>
);
}
});
var NavBox= React.createClass({
loadNavsFromServer: function() {
$.ajax({
url: "http://server/api/widgetlists/?format=json",
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error("http://server/api/widgetlists/?format=json", status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadNavsFromServer();
},
render: function() {
return (
<div className="col-md-2">
<div className="sidebar content-box" style={{display: "block"}}>
<NavList data={this.state.data} />
</div>
</div>
);
}
});
module.exports = {
NavBox: NavBox
}
Content.js
var Widgets = React.createClass({
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
$.ajax({
url: 'http://server/api/widgets/?list="xxxx"&format=json',
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('http://server/api/widgets/?list="xxxx"&format=json', status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="col-md-10">
<div className="row">
<div className="col-md-6">
<div className="content-box-large">
<div className="panel-body">
<BootstrapTable data={this.state.data} striped={true}>
<TableHeaderColumn isKey={true} hidden={true} dataField="id">Widget ID</TableHeaderColumn>
<TableHeaderColumn dataField="title">Title</TableHeaderColumn>
<TableHeaderColumn dataField="username">Username</TableHeaderColumn>
<TableHeaderColumn dataField="price">Price</TableHeaderColumn>
</BootstrapTable>
</div>
</div>
</div>
</div>
</div>
);
}
});
module.exports = {
Widgets: Widgets
}
App.js
var Navigation = require("./components/Navigation/Navigation");
var Content = require("./components/Content/Content");
ReactDOM.render(
<Navigation.NavBox/>,
document.getElementById('navigation')
);
ReactDOM.render(
<Content.Widgets/>,
document.getElementById('content')
);
Depending on what link is clicked in Nav.js I want it to update the data in Content.js. NavItem in Nav.js would pass the list name into Content.js (the "xxxx" so that the table would load with the specific data based on that Item.
As #FakeRainBrigand mentions in his comment, this is a pretty typical use case for some type of event-based pattern like Flux.
https://facebook.github.io/flux/docs/overview.html
Basically, you want to create some type of singleton data store that keeps track of the list name. Attach a click handler to the NavItem component that causes a change to the list name in the store. The store should emit a change event in response to any changes. The Widgets component should listen for changes on the store, and when the Widgets component hears a change on the store, it should make another AJAX request with the new list item.
Let me know if you have any specific questions about implementation.
Related
Imagine I have a very simple React component that shows a list of elements that are stored in this.state.myList (see example below)
Hitting a "Refresh" button at the bottom causes React to query the backend server and retrieve an updated list that it will then display. The actual contents or implementation of this list are irrelevant.
var Foo = React.createClass({
handleButtonClick: function() {
return $.ajax({
type: "POST",
url: "/some/refresh/url",
data: JSON.stringify({}),
dataType: "json",
contentType: "application/json",
success: (function(response){
self.setState({ myList: response.list });
})
});
},
render: function() {
return (
<div className="container">
<ul>
{
this.state.myList.map(function(item) {
return <li id="{item.id}">{item.name}</li>
});
}
</ul>
<input type="submit" value="REFRESH LIST" onClick={this.handleButtonClick} />
</div>
);
}
});
Let's say the AJAX call (for whatever reason) takes a few seconds. In that meantime, I'd love to show a standard "loading" or "spinner" gif to let the user know it's working.
What's the best approach to doing that here?
Right before the AJAX call I could manually update the DOM and insert a spinner gif but that doesn't seem like the "React way to do it". And plus I don't know what impact that would have on the ReactDOM that react maintains.
I could track a state for isLoading and show the spinner instead of the list if it is loading. But then I would need it to render() something and then immediately kick off another call to an AJAX action.
Any help appreciated.
Thanks!
The way I always solve this is for the component to track fetchInProgress in its state.
Before you make your fetch, you set this value to true; when the fetch completes (either success or fail), you set the value back to false.
The component's render method then honors this flag; if the flag is true, it renders a spinner instead of a dataset.
var Foo = React.createClass({
handleButtonClick: function() {
// before making call, set fetch flag
self.setState({ fetchInProgress: true });
return $.ajax({
type: "POST",
url: "/some/refresh/url",
data: JSON.stringify({}),
dataType: "json",
contentType: "application/json",
success: (function(response) {
// when updating with dataset, also reset fetch flag
self.setState({
fetchInProgress: false,
myList: response.list
});
}),
failure: ((function(reason) {
// make sure to reset even if fetch fails!
self.setState({
fetchInProgress: false
});
})
});
},
render: function() {
return (
<div className="container">
<ul>
{
this.state.fetchInProgress
: <Spinner />
: this.state.myList.map(function(item) {
return <li id="{item.id}">{item.name}</li>
})
}
</ul>
<input type="submit" value="REFRESH LIST" onClick={this.handleButtonClick} />
</div>
);
}
});
The React model is built around having UI as a representation of your state. That means you should model your state as "what is the necessary data" and the return value of render() is just how you display that data.
In your case, you should keep track of isLoading and in render() you conditionally display the spinner based on the value in your state.
var Foo = React.createClass({
getInitialState: function() {
return {isLoading: false};
},
handleButtonClick: function() {
this.setState({ isLoading: true });
return $.ajax({
type: "POST",
url: "/some/refresh/url",
data: JSON.stringify({}),
dataType: "json",
contentType: "application/json",
success: (function(response){
self.setState({ myList: response.list, isLoading: false });
})
});
},
render: function() {
return (
<div className="container">
<ul>
{
this.state.myList.map(function(item) {
return <li id="{item.id}">{item.name}</li>
});
}
</ul>
{this.state.isLoading && <Spinner />}
<input type="submit" value="REFRESH LIST" onClick={this.handleButtonClick} />
</div>
);
}
});
small edit from #Tom's answer above.
render: function() {
return (
<div className="container">
<ul>
{
this.state.fetchInProgress ?
<Spinner />
:
this.state.myList.map(function(item) {
return <li id="{item.id}">{item.name}</li>
})
}
</ul>
<input type="submit" value="REFRESH LIST" onClick={this.handleButtonClick} />
</div>
);
}
I am new in react and I'm learning on official website with tutorial help.
So, I have structure in my app:
-js
-notes.js
-comment.json
index.html
notes.js have next code:
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)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function () {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function(){
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});
var CommentList = React.createClass({
render: function(){
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
render: function () {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
var Comment = React.createClass({
render: function () {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<p>
{this.props.children}
</p>
</div>
);
}
});
ReactDOM.render(
<CommentBox url="comment.json" pollInterval={2000} />,
document.getElementById('content')
);
Here my file data "comment.json":
[
{"id": "1", "author": "Pete Hunt", "text": "This is one comment"},
{"id": "2", "author": "Jordan Walke", "text": "This is *another* comment"}
]
But it doesn't work and in console I have next message:
jquery.js:9392 GET http://helper.com/views/com.json?_=1471986105038 404 (Not Found)
and another one:
notes.js:22 com.json error Not Found
Maybe I write wrong url parametr in:
<CommentBox url="comment.json" pollInterval={2000} />
Can anybody help me?
I'm doing a react app and id I'd like to know how to think multipages websites. Actually i'im I'm doing a course searcher,i searcher; I use routie to render the different components that renders render the page. The problem is that they arent aren't related by hierarchy, so the ajax data isn't accessible to the component that renders the result.I've I've tried vainly to use a js var data but doesnt var data but that doesn't work too either.Ive read https://facebook.github.io/react/tips/communicate-between-components.html
but i don't see what to do with own event system. If someone could illustrate the last paragraph of this doc it is great for all the people that are in this case.
var data = {};
var CourseSearcher = React.createClass({
getInitialState: function(){
return {
return { places: '',
branch: 0,
dayOfMonth: '',
timeStart: '',
timeEnd: '',
data: []};
},;
},
handlePlacesChange: function(e){
this.setState({places: e.target.value});
},
handleBranchChange: function(e){
this.setState({branch: e.target.value});
},
handleDayOfMonthChange: function(e){
this.setState({dayOfMonth: e.target.value});
},
handleTimeStartChange: function(e){
this.setState({timeStart: e.target.value});
},
handleTimeEndChange: function(e){
this.setState({timeEnd: e.target.value});
},
handleSubmit: function(e){
// stop the default browser action
e.preventDefault();
// Do an ajax post
$.ajax({
url:'php/results.php',
dataType: 'json',
type: 'GET',
data: {
data: {places: this.state.places,
branch:this.state.branch,
dayOfMonth:this.state.dayOfMonth,
timeStart:this.state.timeStart,
timeEnd:this.state.timeEnd},
},
success: function(data){
this.setState({data: data});
data = this.state.data;
routie('results');
}.bind(this),
error: function (xhr,status,err){
console.error('php/results.php',status,err.toString());
}.bind(this)
});
},
render: function(){
return(
<div>
<form method="get" onSubmit={this.handleSubmit}>
<label>Où?</label>
<input
type="text"
placeholder="Lieux"
value={this.state.places}
onChange={this.handlePlacesChange}
/>
<label>Quoi?</label>
<select value={this.state.branch} onChange={this.handleBranchChange}>
<option>Matière</option>
<option>Français</option>
<option>Anglais</option>
</select>
<label>Quand ?</label>
<input
type="date"
value={this.state.dayOfMonth}
onChange={this.handleDayOfMonthChange}
/>
<input
type="time"
value={this.state.timeStart}
onChange={this.handleTimeStartChange}
/> -
<input
type="time"
value={this.state.timeEnd}
onChange={this.handleTimeEndChange}/>
<button type="submit">Go!</button>
</form>
</div>
);
}
});
console.log(data);
var ResultList = React.createClass({
render: function() {
console.log(data);
return(
<h1>Hello</h1>);
}
);
}
});
var ResultBox = React.createClass({
render: function() {
return (
<div>
<h4>{}</h4>
</div>
);
}
});
routie({
'':function() {
React.render(<CourseSearcher />,
document.getElementById('content'));
},
'results': function() {
React.render(
React.render(<ResultList results={data} />,
document.getElementById('content'));
}
});
Done well with react router ;)
I've done it with react router where components are related to some dedicated urls
First of all I'm totally new to react so I'm not sure if my code is already written the "react way".
So far I've created a couple of react classes which render a Bootstrap Modal. To set the initial states I call an Ajax function within the componentsDidMount function. This works fine until I try to insert plain HTML into the modal body.
The server request works fine and I get plain HTML code in my this.state.data.content but if I try to insert this into the modal body I receive following error:
Error: Invariant Violation: Objects are not valid as a React child (found: object with keys {__html}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons.
Does anyone know how to fix this? Am I even doing the right thing here?
Thanks!
<script type="text/babel">
var Modal = ReactBootstrap.Modal;
var Button = ReactBootstrap.Button;
var ButtonToolbar = ReactBootstrap.ButtonToolbar;
var L5fmHeaderButton = React.createClass({
render: function() {
var iconClass = "glyphicon " + this.props.buttonIcon;
return(
<button onClick={this.props.onClick} className="lfm-Modal-Button">
<span className={iconClass} aria-hidden="true"></span>
{this.props.buttonText}
</button>
);
}
});
var L5fmModalBody = React.createClass({
rawMarkup: function() {
return { __html: this.props.content };
},
render: function() {
return(
<Modal.Body>
dangerouslySetInnerHTML={this.rawMarkup()}
</Modal.Body>
);
}
});
var L5fmModal = React.createClass({
getInitialState : function() {
return {
data : []
};
},
componentDidMount: function() {
$.ajax({
url: 'L5fm/setInitialState',
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
console.log(data);
console.log(this.state.data);
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
changeDirectory : function() {
if (this.state.privateDir) {
this.setState({privateDir: false});
}
else {
this.setState({privateDir: true});
}
},
render: function() {
if(this.state.data.privateDir) {
var browseIcon = "glyphicon-folder-open";
var browseText = "browse all files";
}
else {
var browseIcon = "glyphicon-briefcase";
var browseText = "browse private files";
}
return(
<Modal {...this.props} bsSize="large" aria-labelledby="contained-modal-title-lg">
<Modal.Header closeButton>
<div className="header-button-group">
<L5fmHeaderButton buttonIcon="glyphicon-cloud-upload" buttonText="upload" />
<L5fmHeaderButton buttonIcon="glyphicon-list" buttonText="list View" />
<L5fmHeaderButton onClick={this.changeDirectory} buttonIcon={browseIcon} buttonText={browseText} />
</div>
</Modal.Header>
<L5fmModalBody content={this.state.data.content}/>
</Modal>
);
}
});
var App = React.createClass({
getInitialState: function() {
return { lgShow: false };
},
render: function() {
let lgClose = () => this.setState({ lgShow: false });
return (
<ButtonToolbar>
<Button bsStyle="primary" onClick={()=>this.setState({ lgShow: true })}>
Launch large demo modal
</Button>
<L5fmModal show={this.state.lgShow} onHide={lgClose} />
</ButtonToolbar>
);
}
});
ReactDOM.render(<App />, document.getElementById("modal"));
</script>
Well, as it seems, you are missing a div-tag where you wish to render your raw html
considering changing the Modal.Body code like this
var L5fmModalBody = React.createClass({
rawMarkup: function() {
return { __html: this.props.content };
},
render: function() {
return(
<Modal.Body>
<div dangerouslySetInnerHTML={createMarkup()} />
</Modal.Body>
);
}
});
otherwise the rendering gets broken because your markup cannot really be set as a child on the Modal.Body element
My site works flawlessly in web browsers, but IE11 shows this error in console.
What should I do with this?
React.render(
<NewsFeed tagFilter={tagFilter}/>,
document.getElementById('newsFeed')
);
'NewsFeed' is undefined
EDIT:
Here is the newsfeed (.js JSX file, included in page's header)
var Article = React.createClass({
askDelete: function(id,title) {
if (confirm("Delete the '"+title+"'?")) {
$.ajax({
url: "/-----removed----/"+id,
type: "DELETE",
async: false,
cache: false,
dataType: 'json'
});
location.reload();
}
},
handleClick: function(id) {
React.unmountComponentAtNode(document.getElementById('editor'));
React.render(
<PostEditor
postId={id}
/>,
document.getElementById('editor')
);
functAdmin.showEditor();
},
render: function() {
var editIcon=[];
var boundClick = this.handleClick.bind(this, this.props.id);
var boundDeleteClick = this.askDelete.bind(this, this.props.id,this.props.title);
editIcon.push(<span className="adminActionsNewsfeed" key={"editIcon"+this.props.id}><i className="pointer gray fa fa-edit" onClick={boundClick}></i><i className="pointer fa fa-remove red" onClick={boundDeleteClick} key={"deleteNewsIcon"+this.props.id}></i></span>);
var tags=[];
if (typeof this.props.tags != 'undefined') {
var tagArray=this.props.tags.split(",");
for(tag of tagArray){
tags.push({tag});
}
}
return (
<div className="article" key={"newsArticle"+this.props.id}>
{editIcon}
<span className="articleTitle">
{this.props.title}
</span>
<span className="articleTime">
{funct.formatDate(this.props.time)}
</span>
<div className="articleBody">
{this.props.children}
</div>
<div className="tagLine">{tags}</div>
</div>
);
}
});
var ArticleList = React.createClass({
render: function() {
this.props.data.sort(funct.SortByTime);
var postNodes = this.props.data.map(function (item) {
var tags = item.news_tags.toLowerCase().split(",");
if ((tagFilter=="") || (tags.indexOf(tagFilter) > -1)) {
return (
<Article
key={"news"+item.news_id}
title={item.news_title}
tags={item.news_tags}
time={item.news_time}
id={item.news_id}
>
{item.news_body}
</Article>
);
}
});
return (
<span>
{postNodes}
</span>
);
}
});
var NewsFeed = React.createClass({
launchAdd: function() {
React.unmountComponentAtNode(document.getElementById('editor'));
React.render(
<PostEditor
postId={""}
/>,
document.getElementById('editor')
);
functAdmin.showEditor();
},
getInitialState: function() {
return {data: []};
},
refreshPostStatus: function() {
$.ajax({
url: "/-----removed----",
type: "GET",
cache: false,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.log( xhr.responseText);
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.refreshPostStatus();
},
render: function() {
var addIcon=[];
addIcon.push(<div className="addPostLine"><i className={funct.statusIcon[5]+" fa-2x"} onClick={this.launchAdd} />Add new post</div>);
return (
<span>
<i className="fa fa-file fa-lg blue"></i>
{addIcon}
<h1>Netbiter funct System Information</h1>
<ArticleList data={this.state.data}/>
</span>
);
}
});
Solved it.
IE11 didn't like this part of newsfeed.js:
for(tag of tagArray){
tags.push({tag});
}
It works after replacing it with this:
for(var i in tagArray){
var tag=tagArray[i];
tags.push({tag});
}
just import 'core-js/es6/' to your index.js or main entry file
import 'core-js/es6/';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
ReactDOM.render(<App />, document.getElementById('root'));