How to reload Ajax Request while routing between pages in ReactJS - javascript

I'm new to ReactJS and I'm trying to use React Router on a project I'm working on. I'm using a nested route to load a component, but I suspect that because of the structure of my project, the nested route is not using it's handler at all. So when I use the Back button there is no history of the first ajax request, but it keeps showing the results of the second one.
I have this parent component Competitions which performs an ajax request:
var Competitions = React.createClass({
getInitialState: function() {
return {
compData: [],
id: "",
hide: false,
}
},
componentDidMount: function() {
axios.get(this.props.source, {
headers: {'X-Auth-Token': '********************',
'Content-type': 'application/json'}
})
.then(result => {
this.setState({compData: result.data});
})
.catch(error => {
console.log(error);
});
},
changeId: function (newId) {
this.setState({
id: newId
});
},
render: function() {
return (
<CompTable compData={this.state.compData} id={this.state.id} onClick= {this.changeId} hide={this.state.hide}/>
);
}
});
module.exports = Competitions;
The Competitions component passes the results data to it's child CompTable
var CompTable = React.createClass({
propTypes: {
compData: React.PropTypes.array.isRequired
},
getInitialState: function() {
return {
showTeams: this.props.showTeams,
hide: this.props.hide,
};
},
teamsClick: function() {
this.setState({
showTeams: !this.props.showTeams,
hide: !this.props.hide,
});
},
handleClick: function(e) {
this.props.onClick(e);
},
render: function() {
var hide = this.state.hide;
var list = this.props.compData.map(function (comp, i) {
return (
<tr key={i+1}>
<th scope="row">{i+1}</th>
<td className={comp.id} onClick={function() { this.teamsClick(); this.handleClick(comp.id); }.bind(this)}> <Link to={'/competitions/'+comp.league}> {comp.caption} </Link> </td>
<td>{comp.league}</td>
<td>{comp.numberOfTeams}</td>
</tr>
);
}, this);
if (hide) {
return (
<div className="row">
<div > { this.state.showTeams ? <Teams id={this.props.id}/> : null } </div>
</div>
)
}
else {
return (
<tbody>{list}</tbody>
)
}
});
module.exports = CompTable;
Inside the CompTable component I have a conditional render. So the first time the ajax call is made, it loads the Competitions Table and then when a competition is clicked, I grab the clicked team id and load the child component Teams which performs the second ajax request:
var Teams = React.createClass({
getInitialState: function() {
return {
teamData: [],
}
},
componentWillMount: function(){
this.dataSource();
},
componentWillReceiveProps: function(nextProps){
this.dataSource(nextProps);
},
dataSource: function(props) {
props = props || this.props;
var url = 'http://api.football-data.org/v1/competitions/x/teams';
var source = url.replace(url.split('/')[5], this.props.id);
axios.get(source, {
headers: {'X-Auth-Token': '********************',
'Content-type': 'application/json'}
})
.then(result => {
this.setState({teamData: result.data.teams});
})
.catch(error => {
console.log(error);
});
},
render: function() {
return (
<TeamsTable teamData={this.state.teamData}/>
);
}
});
module.exports = Teams;
I'm posting the Index file with the Router:
var app = document.getElementById('app');
var CompetitionsWrapper = React.createClass({
render: function () {
return (
<Competitions source="http://api.football-data.org/v1/competitions/?season=2016"/>
);
}
});
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={Layout}>
<IndexRoute component={Home}></IndexRoute>
<Route path="/competitions" component={CompetitionsWrapper}>
<Route path="/competitions/:teamLeague" component={Teams} ></Route>
</Route>
</Route>
</Router>,
app);
Every time the competitions table loads and I'm able to load a Teams Table by clicking on a competition, when I'm using the back button there isn't a new ajax request, but I still see the loaded Teams Table and I can see from Chrome developer tools that the same team is reloaded. Is there any way to recall the Competitions ajax request when I use the back button or is there another way to make browser history work correctly?

Related

Update state when data changed in React+Redux

I'm too new to react. I'm curious about a few things. The first topic I was wondering, why the HTML tags in js?
I need to do a project. I have a method that returns json with .NET
I have a code like below. How do I update the div when I add something into it?
This .net code.
private static readonly IList<Mock.Model.Skills> _Skills;
[Route("api/Skills/add")]
[HttpPost]
public ActionResult Skills(Mock.Model.Skills Model)
{
if (ModelState.IsValid)
{
_Skills.Add(Model);
return Json("true");
}
return Json(false);
}
And, js (react) code.
var Rows = React.createClass({
render: function () {
return (
<span className="label label-info tags">{this.props.item.tag}</span>
);
}
});
var SkillsRow = React.createClass({
getInitialState: function () {
return {
items: []
}
},
componentDidMount: function () {
$.get(this.props.dataUrl, function (data) {
if (this.isMounted()) {
this.setState({
items: data
});
}
}.bind(this));
},
render: function () {
var rows = [];
this.state.items.forEach(function (item, index) {
rows.push(<Rows class="label label-info tags" key={index} item={item} />);
});
return (<span>{rows}</span>);
}
});
ReactDOM.render(
<SkillsRow dataUrl="/api/Skills" />,
document.getElementById('skills-data')
);
This works well, but do not add.
I wonder if this is the correct method?
Thank you to everyone who showed interest.
If you want to add items to api, you can call something like this:
var SkillsRow = React.createClass({
getInitialState: function() {
return {
items: [],
currentEditor: ''
}
},
componentDidMount: function() {
this.updateSkillList()
},
updateSkillList: function() {
$.get(this.props.dataUrl, function(data) {
this.setState({
items: data
})
}).bind(this)
},
handleEditorChange: function(event) {
this.setState({
currentEditor: event.target.value
})
},
handlePostForm: function() {
$.post('api/Skills/add', {skill: this.state.currentEditor}, function(data) {
this.updateSkillList()
})
},
renderSkillList: function() {
this.state.items.map(function(item, idx) {
return <Row className="label label-info tags" key={idx} item={item} />
})
},
render: function() {
return (
<div>
<span>
<input value={this.state.currentEditor} onChange={this.handleEditorChange} />
<button onClick={this.handlePostForm} />
</span>
<span>{this.renderSkillList()}</span>
</div>
)
}
})
Edited:
Now i understood the question, you code will look something like this, also you will have to fix backend code to only receive the skill name, and then create the object (you can't create a C# object in javascript)

React Js SetState using React Router

I have 2 React Classes:
const PostList = React.createClass({
getInitialState: function () {
return {
status: "loading",
posts: []
}
},
render: function() {
return (
<div id="container">
<ReactRouter.Link to="upload"></ReactRouter.Link>
{this.state.status}
{
this.state.posts.map(function (post) {
return (
<Post post={post} />
);
})
}
</div>
);
}
});
const Upload = React.createClass({
render: function() {
return (
<div className="upload">Upload</div>
);
}
})
const MainContent = React.createClass({
render: function() {
return (
<div>
<Header />
<div id="inner-content">
{this.props.children}
</div>
<Footer />
</div>
);
}
})
const routes = (
<Route path="/" component={MainContent}>
<ReactRouter.IndexRoute component={PostList} />
<Route path="upload" component={Upload} />
</Route>
)
var render = ReactDOM.render(<Router history={History}>{routes}</Router>, document.getElementById("content"));
The question is how can i access the setState function on PostList to render new posts?
Posts will be refreshed through socket.io conenction so when a new posts arrives, i just want to set the state of the PostList to rerender the full html code with he new post(s)
Sorry for bad english...
Add componentDidMount function and set up a socket listener inside it such that when a new message arrives, add it to the current list of posts
const PostList = React.createClass({
getInitialState: function () {
return {
status: "loading",
posts: []
}
},
componentDidMount: function () {
var that = this;
// Be sure to set up data fetching code here
// Set up listener for posts updates here
socket.on('update', function (data) {
// Assuming data contains the post content
that.setState({
posts: that.state.posts.concat(data)
});
});
},
render: function() {
return (
<div id="container">
<ReactRouter.Link to="upload"></ReactRouter.Link>
{this.state.status}
{
this.state.posts.map(function (post) {
return (
<Post post={post} />
);
})
}
</div>
);
}
});

Listview not working... (Objects are not valid as React Child)

I am currently building an app in which I am trying to implement the React Native Listview functionality. I am however at a standstill...
The code below yields simply the error: Objects are not valid as React Child (found: objects with keyes {name, img, .... } )
var ComponentOne = React.createClass({
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
loaded: false;
return {
dataSource: ds.cloneWithRows(this.props.friendFeed),
};
},
render: function() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.CardViewer}
style={styles.card} />
)
},
CardViewer(rowData, sectionID, rowID) {
console.log(rowData.friend);
return (
<CardView
name={rowData.friend}
fullname='xyz'
distance='xyz'
title={rowData.post}
message={rowData.post}
score='xyz'
stars='xyz'
starcolors={colors.General.black}
vouched="Example" />
)
}
})
But if I instead try to write the Cardviewer as: ... <CardView name={rowData.friend.name} ... it will return the error that undefined is not an object. But I dont know how to further set some loading state on each of those...
I create the data that I use in the following manner:
var Home = React.createClass({
getInitialState: function() {
return {
componentSelected: 'One',
tokenSupreme: 'Loading big token...',
userName: "Loading...",
friendFeed: 'Loading...',
};
loaded: false;
},
componentDidMount: function() {
Method.loginUser(AccessToken)
.then((res) => this.setState({
userName: JSON.parse(res).user.name,
tokenSupreme: JSON.parse(res).token,
}))
.catch((error) => console.log(error))
.done();
loaded: true;
exports.tokenSupreme = this.state.tokenSupreme;
},
changeComponent: function(component) {
Method.getFriendFeed(this.state.tokenSupreme)
.then((res) => this.setState({
friendFeed: JSON.parse(res).friendPosts,
}))
.catch((error) => console.log(error))
.done();
this.setState({
componentSelected: component
})
},
renderComponent: function(component) {
if(component === 'One') {
return <ComponentOne friendFeed={this.state.friendFeed} />
} else if(component === 'Two') {
return <ComponentTwo />
}
},
Where have I gone wrong? My guess is because that I dont predefine som state before loading which throws the object not defined, but then again, the Listview does not seem to have that as an option, or then again, I have (most likely) probably misintepreted it. So, please help! :)
EDIT:
Trying to manipulate the execution order via the component-lifecycles I made it so that loaded = true when the frienFeed is executed, and by then I have my render as an if function so that it renders "loading" untill otherwise. But it never seems to re-evaluate the if statement. Because I can see in my log how loaded = true, but it does not react. Code as the following in the ComponentOne:
render: function() {
if (this.state.loaded != true) {
return this.renderLoadingView();
} else if(this.state.loaded === true) {
console.log('trying...');
console.log(this.state.props.friendFeed);
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
style={styles.card} />
)
}
},

React render has the correct data but won't render the JSX

So, I believe this is a formatting issue OR I'm not clear about how the return works when dynamically building.
The render function in Results works, if I replace the code with anythign else it renders where I want. Similarly, the console.log's in the Results function outputs the data correctly. There's no error, it just doesn't render the html and it doesn't hit the debugger in SynonymElement.
What am I missing in here / what core concept am I misconstruing?
(This is just an input form that takes a word, user hits submit, it returns an object with the word as a key and the value an array of synonynms. that get rendered in the ul)
'use strict'
const Smithy = React.createClass({
dsiplayName: "Smithy",
getInitialState: function() {
return { data: []};
},
handleSubmit: function(data) {
$.get('/get-synonyms', { data: data.data }).done(function(data) {
this.setState({ data: data})
}.bind(this));
},
render: function() {
return (
<div className="smithy">
<h1>Craft Tweet</h1>
<SmithyForm onSubmit={this.handleSubmit} />
<Results data={this.state.data} />
</div>
)
}
})
const SmithyForm = React.createClass({
displayName: "SmithyForm",
getInitialState: function() {
return { placeholder: "tweet", value: "" };
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
handleSubmit: function(event) {
event.preventDefault();
var tweet = this.state.value.trim();
this.props.onSubmit({ data: tweet });
this.setState({value: ''});
},
render: function() {
var placeholder = this.state.placeholder;
var value = this.state.value;
return (
<form className="smithyForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder={placeholder} value={value} onChange={this.handleChange} />
<button>smithy</button>
</form>
);
}
})
const SynonymElement = React.createClass({
render: function() {
debugger
return (
<li>{this.props.data}</li>
)
}
})
const Results = React.createClass({
render: function() {
var words = this.props.data;
return (
<div className="results">
{
Object.keys(words).map(function(value) {
{ console.log(value) }
<div className={value}>
<ul>
{
words[value].map(function(syn) {
{ console.log(syn) }
return <SynonymElement data={syn} />
})
}
</ul>
</div>
})
}
</div>
)
}
})
ReactDOM.render(<Smithy />, document.getElementsByClassName('container')[0])
Might have some other complicating issues but assuming everything else is wired up correctly, you need to return the result of the function you pass into the first map (over the collection Object.keys(words)) just as you have for the later map otherwise the function is executed and nothing useful is returned.
Possibly just a dupe of loop inside React JSX
return (
<div className="results">
{
Object.keys(words).map(function(value) {
return ( // <-- this
<div className={value}>

setState doesn't set object on page with dynamic segments

I have problem with getting state for component which is loaded by react-router on path with dynamic segment /item/:id:
var Item = React.createClass({
mixins: [State],
getInitialState: function () {
return {data: {}};
},
loadTrackData: function () {
api.getItemById(this.getParams().id, function (data) {
this.setState({data: data});
}.bind(this));
},
componentDidMount: function () {
this.loadTrackData();
},
componentWillReceiveProps: function () {
this.loadTrackData();
},
render: function () {
console.log(this.state); // empty `data` object
return (
<div>
<h2>{this.state.data.title.prop}</h2> // doesn't work
// but works with
// <h2>{this.state.data.title}</h2>
</div>
);
}
});
Any of these methods componentDidMount and componentWillReceiveProps are not called at all!
And render logs empty data object as it's just initialized..
Router looks like:
var routes = (
<Route path="/" handler={Index}>
<Route name="stream" path="/item/:id" handler={Item} />
<DefaultRoute handler={Dashboard} />
</Route>
);

Categories

Resources