I am creating an Github issue viewer with React.
I have a component that sets the repo, then I want to create separate components to get the issue name, number, login etc. These components will ultimately be used in the main component/view. I'm a bit stuck, below is what I have so far.
var GetRepo = React.createClass({
getRepo: function(){
var issues = $.getJSON('https://api.github.com/repos/facebook/react/issues', function (data) {
})
},
render: function() {
return <div>My repo: {this.props.repo}</div>
}
});
ReactDOM.render(<GetRepo repo="facebook/react/issues" />, document.getElementById('main'));
var IssueName = React.createClass({
});
//IssueName gets the data.title (the issue name) using repo GetRepo
var IssueNumber = React.createClass({
});
//IssueNumber gets the data.number (the issue number) using repo from GetRepo
Certainly not the only way to do it, but the following should work:
var GetRepo = React.createClass({
getInitialState: function() {
return {
repo: {}
};
},
componentDidMount: function(){
var that = this;
var issues = $.getJSON('https://api.github.com/repos/facebook/react/issues', function (data) {
that.setState({
repo: data
});
});
},
render: function() {
return (
<div>
<IssueName repo={this.state.repo} />
<IssueNumber repo={this.state.repo} />
</div>
);
}
});
//IssueName gets the data.title (the issue name) using repo GetRepo
var IssueName = React.createClass({
render: function() {
return (
<div>this.props.repo.title</div>
);
}
});
//IssueNumber gets the data.number (the issue number) using repo from GetRepo
var IssueNumber = React.createClass({
render: function() {
return (
<div>this.props.repo.number</div>
);
}
});
ReactDOM.render(<GetRepo repo="facebook/react/issues" />, document.getElementById('main'));
Related
I am working on a project in which I get into given situation:
myjson:
{
completeName: "my name is {this.state.myName+ " "+this.state.surname}"
}
component.js
var Component = React.createClass({
getInitialState: function() {
return({
myName: "AKHA",
surname: "HUND"
})
},
render: function() {
return (
<div>{myData.completeName}</div>
)
}
})
but I am getting output as:
my name is {this.state.myName+ " "+this.state.surname}
instead of:
my name is AKHA HUND***
please help me.
Note: using ES5.
Thanks
What you store in your .json is a string, not an expression, it is not evaluated in any way, you have to modify the template in your JS code.
Using ES5 you could do something like this:
.json
{
completeName: "my name is %myName %surname"
}
component.js
var Component= React.createClass({
getInitialState: function(){
return({
myName: "AKHA",
surname: "HUND"
})
},
render: function(){
const result = myData.completeName
.replace("%myName", this.state.myName)
.replace("%surname", this.state.surname);
return (
<div>{ result }</div>
)
}
})
Below is my content.jsx
var React = require('react');
var Reflux = require('reflux');
var ApplyInfo = require('./applyInfo');
var Actions = require('../actions/actions');
var DataStore = require('../stores/data-store');
module.exports = React.createClass({
mixins: [
Reflux.listenTo(DataStore, 'onChange'),
],
onChange: function(event, allDatas) {
console.log("o");
this.setState({
applyDatas: allDatas
})
},
getInitialState: function() {
console.log('g');
return {
section: "go_3",
applyDatas: {test : "AAA"}
}
},
componentWillMount: function() {
console.log('c');
Actions.getDatas();
},
render: function() {
console.log("content = " + this.state.applyDatas.test);
return <div>
<div className="content">
<ApplyInfo showValue={this.state.applyDatas.test} print={console.log("showValue = " + this.state.applyDatas.test)} />
.......
Below is my applyInfo.jsx
var React = require('react');
var Reflux = require('reflux');
var TextInput = require('./TextInput');
var Option = require('./option');
var Actions = require('../actions/actions');
module.exports = React.createClass({
getInitialState: function() {
return {
// .....
applyInfoDatas: this.props.showValue
}
},
render: function() {
console.log("value = " + this.state.applyInfoDatas);
return <div className="section_3">
<div className="info_input">
<div className="demo_a demo2 lll">
<TextInput id="loanAmount" title="loan" showValue={this.state.applyInfoDatas}/>
</div>
</div>
Below is my data-store.jsx
var Refulx = require('reflux');
var Actions = require('../actions/actions');
var allDatas = {test : "AAB"};
module.exports = Refulx.createStore({
listenables: Actions,
getDatas: function() {
console.log("ready to send allDatas");
this.trigger('change', allDatas);
}
});
and here is my console.log result from chrome
g
c
content = AAA
showValue = AAA
value = AAA
ready to send allDatas
o
content = AAB
showValue = AAB
value = AAA
Why I still got "value = AAA" in the end , i thought it should be "AAB" which I already change it when call Actions.getDatas and setState.
Looks like you need to change your ApplyInfo component. The flow in your test is as follows:
Content.jsx has state of 'AAA', and passed it down as prop to ApplyInfo.jsx
ApplyInfo component sets its own initial state to 'AAA'
ApplyInfo renders state = 'AAA'
store changes to 'AAB' and emits change
Content.jsx updates state to 'AAB', and passes state down as prop to ApplyInfo
The state inside ApplyInfo is not updated; getInitialState is only called once. The second time the component is already rendered, and will only be updated, so getInitialState is not called.
so ApplyInfo still has state 'AAA', which it renders.
Two ways to deal with this:
You could simply render showValues as a prop. (but then you will need an onChange function in your TextInput component)
Add a componentWillReceiveProps to update your state (see answer by #zvona)
Ad 1. To make ApplyInfos.jsx simply render new showValue:
module.exports = React.createClass({
render: function() {
console.log("value = " + this.props.showValue);
return <div className="section_3">
<div className="info_input">
<div className="demo_a demo2 lll">
<TextInput id="loanAmount" title="loan" showValue={this.props.showValue}/>
</div>
</div>
On ApplyInfo, try this to receive latest props to state:
...
componentWillReceiveProps: function() {
this.setState({
applyInfoDatas: this.props.showValue
});
}
...
Ok, I do know through refs communicate between parent and child or use this.props.onClick = {this.props.onClick}, I got stuck in situation communicate between grandparent and child like this:
Says we have some blogs, once we click a blog title, the corresponding blog content will show, so we create three components: BlogAdmin, BlogTitle and Blog (Here let's just focusing on BlogAdmin and BlogTitle)
When BlogTitle is clicked, I want to notify BlogAdmin set currentblog to specify blog. But I got stuck on how to pass the data and how to trigger the event, better with out using pubSub.
Below is my example, I removed some data get/set and grammars making it clear.
var BlogTitle = React.createClass({
render: function() {
return
<li>{this.props.blog.title}</li>
}
});
var BlogTitles = React.createClass({
render: function() {
return
<ul>
{this.state.blogs.map}
<BlogTitle blog={blog} />
}
})
var BlogAdmin = React.createClass({
render: function() {
return
<BlogTitles />
<BlogContent />
}
})
The easy solution is to add a callback function and send it down all the way like this:
var BlogTitle = React.createClass({
render: function() {
return
<li onClick={this.handleTitleClick}>{this.props.blog.title}</li>
},
handleTitleClick: function() {
this.props.onBlogTitleSelection(this.props.blog);
}
});
var BlogTitles = React.createClass({
render: function() {
return
<ul>
{this.state.blogs.map}
<BlogTitle blog={blog} onBlogTitleSelection={this.props.onBlogTitleSelection} />
}
})
var BlogAdmin = React.createClass({
selectBlogTitle: function(blog) {
// act!
},
render: function() {
return
<BlogTitles onBlogTitleSelection={this.selectBlogTitle} />
<BlogContent />
}
})
I have a component which is updated by a parent component by passing a prop. Within the componentWillReceiveProps i would like to change a state (availableData) which contains the newly added data from the prop (newData).
The prop is named newData, and the state which is updated is named availableData.
When i attempt to access the availableData where i concatenate new (unique) data i get following error:
Uncaught TypeError: Cannot read property 'availableData' of undefinedInline JSX script:79
And the code snippet:
var DataList = React.createClass({
getInitialState: function() {
return {availableData: []};
},
componentWillReceiveProps: function(nextProps) {
var availableData = this.state.availableData;
var newData = nextProps.newData;
if (_.isEmpty(availableData)) {
this.setState({availableData: nextProps.newData});
} else {
_.each(newData, function(_newData) {
var isDuplicate = false;
_.each(availableData, function(_availableData) {
if(isSameData(_availableData, _newData)) {
isDuplicate = true;
}
});
if (!isDuplicate) {
console.log(_newData);
this.setState({ availableData: this.state.availableData.concat([_newData]) });
}
});
}
},
handleClick: function (_data) {
},
render: function() {
var dataItems = this.state.availableData.map(function (_data, index) {
return <DataItem data={_data} key={index} onClick={this.handleClick.bind(this, _data)} />;
}, this);
return (
<div className="col-lg-3">
<ul className="list-group">
<li className="list-group-item active">Data</li>
{dataItems}
</ul>
</div>
);
}
});
Failing on:
this.setState({ availableData: this.state.availableData.concat([_newData]) });
UPDATE:
Solved by setting var _this = this; outside the loop and referring to _this, unfortunately all setStates are not being initialized.
this isn't the component in the context of your duplicate. You need to pass the outer most this to your _.each.
_.each(list, iteratee, [context])
I have a dynamic list of children, that are form inputs.
ex:
var FormRows = React.createClass({
getInitialState: function() {
return {
rows: []
}
},
createRows: function() {
this.props.values.maps(value){
rows.push(<FormRow ...handlers... ...props... value={value} />
}
},
addNewRow{
// add a new row
},
render: function() {
return (
<div>
{this.state.rows}
</div>
);
});
var FormRow = React.createClass({
getInitialState: function() {
return {
value: this.props.value || null
}
},
render: function() {
<input type='text' defaultValue={this.state.value} ...changeHandler ... }
}
});
This is a dumbed down version , but the idea, is a its a dynamic form, where the user can click a plus button to add a row, and a minus button, which will set the row to visibility to hidden.
This state is nested n levels deep. What is the best way to actually get the state out of the children, and submit the form? I can use 'ref' add a function to getFormValue(): { return this.state.value } to the FormRow button, but i'm not sure if thats the best practice way.
I find myself using this pattern quite often, an array of undetermined size of children, that need to pass the state up.
Thanks
It’s not a dumb question at all, and a good example of using flux principals in React. Consider something like this:
var App
// The "model"
var Model = {
values: ['foo', 'bar'],
trigger: function() {
App.forceUpdate()
console.log(this.values)
},
update: function(value, index) {
this.values[index] = value
this.trigger()
},
add: function() {
this.values.push('New Row')
this.trigger()
}
}
var FormRows = React.createClass({
addRow: function() {
Model.add()
},
submit: function() {
alert(Model.values);
},
render: function() {
var rows = Model.values.map(function(value, index) {
return <FormRow key={index} onChange={this.onChange} index={index} value={value} />
}, this)
return (
<div>{rows}<button onClick={this.addRow}>Add row</button><button onClick={this.submit}>Submit form</button></div>
)
}
})
var FormRow = React.createClass({
onChange: function(e) {
Model.update(e.target.value, this.props.index)
},
render: function() {
return <input type='text' defaultValue={this.props.value} onChange={this.onChange} />
}
});
App = React.render(<FormRows />, document.body)
I used a simplified model/event example using Array and forceUpdate but the point here is to let the model "own" the form data. The child components can then make API calls on that model and trigger a re-render of the entire App with the new data (Flux).
Then just use the model data on submit.
Demo: http://jsfiddle.net/ekr41bzr/
Bind values of inputs to some model (for example build in Backbone or Flux) and on submit retrieve values from there, without touching inputs.