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>
);
}
});
Related
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?
I have 2 component, how do I pass user entered value through onChange to parent component? I'm able to pass the 'trigger' upon onChange, but how to pass the value along?
https://jsfiddle.net/gboaxm30
var InputComp = React.createClass({
render: function() {
return (
<div>
<input type="text" onChange={this.props.newVal} />
</div>
);
}
});
var App = React.createClass({
getInitialState(){
return {
inputVal: 0
}
},
inputChangedHandler(props) {
//set user changed value to inputVal
console.log(props)
},
render() {
return(
<div>
<InputComp newVal={this.inputChangedHandler}/>
<h4>{this.state.inputVal}</h4>
</div>
)
}
})
ReactDOM.render(
<App />,
document.getElementById('container')
);
Call a function on the onChange event of the child component and then access the value of input like e.target.value and then pass it to the parent component like this.props.newVal(e.target.value);
var InputComp = React.createClass({
handleChange(e) {
this.props.newVal(e.target.value);
},
render: function() {
return (
<div>
<input type="text" onChange={this.handleChange} />
</div>
);
}
});
var App = React.createClass({
getInitialState(){
return {
inputVal: 0
}
},
inputChangedHandler(val) {
console.log(val);
this.setState({inputVal: val});
},
render() {
return(
<div>
<InputComp newVal={this.inputChangedHandler}/>
<h4>{this.state.inputVal}</h4>
</div>
)
}
})
ReactDOM.render(
<App />,
document.getElementById('container')
);
JSFIDDLE
I've made a demo for you here: http://codepen.io/PiotrBerebecki/pen/pEAQzV
The idea is to use the so-called controlled input as defined in the React docs: https://facebook.github.io/react/docs/forms.html#controlled-components
var InputComp = React.createClass({
getInitialState() {
return {
userInput: ''
};
},
onChange(event) {
this.setState({
userInput: event.target.value
});
this.props.newVal(event.target.value);
},
render() {
return (
<div>
InputComp
<input type="text"
value={this.state.userInput}
onChange={this.onChange} />
</div>
);
}
});
var App = React.createClass({
getInitialState() {
return {
inputVal: ''
};
},
inputChangedHandler(valueFromChild) {
console.log('valuefromChild:', valueFromChild);
this.setState({
inputVal: valueFromChild
});
},
render() {
return (
<div>
<InputComp newVal={this.inputChangedHandler}/>
<h4>{this.state.inputVal}</h4>
</div>
);
}
})
ReactDOM.render(
<App />,
document.getElementById('container')
);
You should take the value from the event
inputChangedHandler(e) {
//set user changed value to inputVal
console.log(e.target.value)
},
I thinktThis would be helpful for you.
let InputComp = React.createClass({
getInitialState() {
return {
textVal: "",
};
},
handleChange(e) {
this.setState({ textVal: e.target.value });
this.props.newVal(this.state.textVal);
},
render: function () {
return (
<div>
<input
type="text"
onChange={this.handleChange}
value={this.state.textVal}
/>
</div>
);
},
});
var App = React.createClass({
getInitialState() {
return {
inputVal: 0,
};
},
inputChangedHandler(val) {
this.setState({ inputVal: val });
},
render() {
return (
<div>
<InputComp newVal={this.inputChangedHandler} />
<h4>{this.state.inputVal}</h4>
</div>
);
},
});
ReactDOM.render(<App />, document.getElementById("container"));
I have a NavBarRouteMapper object that I pass to my navbar. However, in the onpress function of one of the buttons I need to access the state, but I'm not sure how to bind 'this' to the object, since it is a non-function. Relevant code as follows
class app extends Component {
state: {
sideMenuIsOpen: boolean,
};
constructor(props: Object) {
super(props);
this.state = {
sideMenuIsOpen: false,
};
};
static NavigationBarRouteMapper = {
LeftButton(route, navigator, index, navState) {
if (index > 0) {
return (
<SimpleButton
// TODO: Make this work, Menu button needs to go to this
// The problem is here. this.state is undefined
onPress={console.log(this.state)}
customText="Back"
style={styles.navBarLeftButton}
textStyle={styles.navBarButtonText}
/>
);
}
},
RightButton(route, navigator, index, navState) {
// TODO change add button for admins
switch (route.title) {
case "Login":
return null;
default:
return (
<SimpleButton
onPress={() => {
navigator.push({
title: "Profile",
component: Profile,
});
}}
customText="Add"
style={styles.navBarRightButton}
textStyle={styles.navBarButtonText}
/>
);
}
},
Title(route, navigator, index, navState) {
return (
<Text style={styles.navBarTitleText}>{route.title}</Text>
);
},
};
render() {
return (
<SideMenu
menu={<Menu navigate={this.navigate} />}
isOpen={this.state.sideMenuIsOpen}
>
<Navigator
ref="rootNavigator"
initialRoute={{
title: "Login",
component: LoginScene,
navigator: this.refs.rootNavigator,
}}
renderScene = {this.renderScene}
navigationBar={
<Navigator.NavigationBar
// Since this is an object, I can't bind 'this' to it,
// and the documentation calls for it to be passed an object
routeMapper={app.NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
</SideMenu>
);
};
}
So you're trying to return the parent's state from the child onClick? If so you can add an onClick which calls the parent onClick that you can pass to the child via props.
var Hello = React.createClass({
getInitialState: function() {
return {test: "testing 1 2 3"};
},
clickMethod: function () {
alert(this.state.test);
},
render: function() {
return <div onClick={this.clickMethod}>Hello {this.props.name}</div>;
}
});
var Child = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
https://jsfiddle.net/reactjs/69z2wepo/
Also if you had a list of components where each component needs to pass a unique piece of data to the parent you can do so using bind.
var Hello = React.createClass({
getInitialState: function() {
return {test: "testing 1 2 3"};
},
clickMethod: function (argument) {
alert(argument);
},
render: function() {
return <div onClick={this.clickMethod.bind(this, "custom argument")}>Hello {this.props.name}</div>;
}
});
var Child = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
https://jsfiddle.net/chrshawkes/64eef3xm/1/
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}>
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>
);