Loop with React? - javascript

Using React, I am implementing Dexie.js for this example. However, I don't believe this to be particularly important. I would like to know how to execute a loop of the objects in my IndexDB database using React.
As shown in my code below, dayta holds my database, and stores friends with name and age values. When my function carveit is run, it takes what users have typed in and places that value on {this.state.etv}. The code works.
However, I do not know how to have {this.state.etv} show ALL entries. As is, it only shows the most recent addition. I understand I would have to execute some kind of loop, and use the map function, but I am unsure how to go about that.
var React = require('react');
module.exports = React.createClass({
getInitialState: function(){
return { etv:'' }
},
carveit:function(){
var enteringtitle = document.getElementById('entertitle').value;
var enteringmessage = document.getElementById('enterentry').value;
var dayta = new Dexie('testcase');
dayta.version(1).stores({
friends:'name, age'
});
dayta.open().catch(function(error){
alert("Oh no son: " +error);
});
dayta.friends.add({
name: enteringtitle,
age: enteringmessage
});
dayta.friends.each((friend)=>{
this.setState({etv: friend.name});
});
},
functionrun:function(){
return (<div>
<ul>
<li>{this.state.etv}</li>
</ul>
<p>Entry Title</p>
<input id="entertitle" type="text" className="form-control"/>
<p>Your Entry</p>
<textarea id="enterentry" className="form-control"></textarea>
<input id="entrytitle" type="submit" value="Publish" onClick={this.carveit} />
</div>);
},
render:function(){
if (this.props.contentadd){
return this.functionrun();
}
else {
return null;
}
}
});

You can perform the loop separately and have it return jsx. You can then use the return value in your template since arrays of jsx partials are supported.
render: function() {
var itemElements = this.state.items.map(function(item, i) {
return (
<li>{item}</li>
);
});
return (
<ul>
{itemElements}
</ul>
);
}

Related

React JS unable to parse span tag

I am working in a project with reactjs. By react code run successfully but unable to parse some tag. My code is
var Item = React.createClass({
render: function () {
var proDiscount;
if(this.props.discount!=0){
proDiscount = '<span>'+this.props.discount+'</span>';
}else{
proDiscount = '';
}
return (
<div className="item">
<div className="tag">{this.props.price} {proDiscount}</div>
</div>
);
}
});
When it render the <span> tag unable to parse. The output keep span tag as it is, rest of the output is fine. Where is my problem Thank you.
It renders the tag, because you're returning a string. This should be the correct code.
var Item = React.createClass({
render: function() {
return (
<div className="item">
<div className="tag">{this.props.price} {this.props.discount && <span>{this.props.discount}</span>}</div>
</div>
);
}
});

Adding/removing input fields

I'm pretty new to ReactJS, I'm liking it a lot, but there are some things like binding that seems to be easier in Angular.
I want to have a form, where a user can click a button to add extra input fields. At any point, they can also "delete" an input field.
On the submit, I want to get these inputs as an array, i.e. pass dynamicInputs to my API which contains an array of name.
This is what I've done (which is probably wrong since I'm treating React like Angular):
var React = require('react');
module.exports = React.createClass({
addInputField: function(e) {
e.preventDefault();
var inputs = this.state.inputs;
inputs.push({name: null});
this.setState({inputs : inputs});
},
removeInputField: function(index) {
var inputs = this.state.inputs;
inputs.splice(index, 1);
this.setState({inputs : inputs});
},
handleSubmit: function (e) {
e.preventDefault();
// What do I do here?
},
getInitialState: function() {
return {inputs : []};
},
render: function (){
var inputs = this.state.inputs;
return (
// Setting up the form
// Blah blah
<div className="form-group">
<label className="col-sm-3 control-label">Dynamic Inputs</label>
<div className="col-sm-4">
{inputs.map(function (input, index) {
var ref = "input_" + index;
return (
<div className="input-group">
<input type="text" className="form-control margin-bottom-12px" placeholder="Enter guid" value={input.name} ref={ref} aria-describedby={ref} />
<span className="input-group-addon" onClick={this.removeInputField.bind(this, index)} id={ref} ><i className="fa fa-times"></i></span>
</div>
)
}.bind(this))}
<button className="btn btn-success btn-block" onClick={this.addInputField}>Add Input</button>
</div>
</div>
);
}
});
Right now removeInputField does NOT work! It just removes the last entry all the time.
Every <div className="input-group"> must have a unique key
<div className="input-group" key={index}>
That's how React distinguishes between collection of rendered nodes.
References:
https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
UPD:
As #WiredPrairie mentioned below in the comments - the suggested solution is far from ideal, since the index is not unique enough. And instead you need to create another array with some unique identifiers (a monotonously growing sequence would be enough) and maintain it in parallel with this.state.inputs and use its values as keys.
So, on adding an element you:
this.keys.push(++this.counter);
on removing - remove from both by the same index. And in the .map you
<div className="input-group" key={this.keys[index]}>

React.js unwantingly reusing data-reactid when rendering subclasses

I have have a Questionnaire object that renders several QuestionnaireOption subclasses. New QuestionnaireOption subclasses are rendered when the state changes in the parent Questionnaire object.
The QuestionnaireOption class maintains state if its "selected" or not.
The Issue: When I change the state in the parent class in order to render new "Option" nodes, the new nodes are assigned the same data-reactid, I expect the Option node to reset its internal state but it isn't assigned a new id and it has contains the wrong state (in this instance, selected is still set to true on a new object despite props being set with new data).
What can I do to work around this issue?
Here's the relevant code:
QuestionnaireOption = React.createClass({
getInitialState: function() {
return {selected: false}
},
handleClick: function(e) {
e.preventDefault();
this.setState({selected: !this.state.selected});
},
render: function() {
var fullClassName = "questionnaireOption " + (this.state.selected? "selected": "unselected");
return (
<div className='questionnaireOptionWrapper large-4 small-4 columns'>
<div className={fullClassName} onClick={this.handleClick}>
<div>{this.props.name}</div>
</div>
</div>
);
}
});
Questionnaire = React.createClass({
getInitialState: function() {
return {currentStage: 0}
},
saveOptionState: function() {
// dump option state into amber.js or localstorage
},
advanceWizard: function() {
this.saveOptionState();
this.setState({currentStage: this.state.currentStage + 1});
},
rewindWizard: function() {
this.saveOptionState();
this.setState({currentStage: this.state.currentStage - 1});
},
seeResults: function() {
console.log(globalOptionState);
},
render: function() {
var currentWizardQuestion = wizardQuestions[this.state.currentStage];
var currentOptionNodes = currentWizardQuestion.options.map(function(option) {
node = (
<QuestionnaireOption
name={option.name}
value={option.value}
/>
);
return node;
});
return (
<div className="questionnaire row">
<div className="questionnaire-question large-8 small-12 columns">
<div className="questionnaire-question-text">
{currentWizardQuestion.text}
</div>
<div className="questionnaire-question-subtext">
{currentWizardQuestion.subtext}
</div>
<div className="row">
{currentOptionNodes}
</div>
<input type="button" value="Back" onClick={this.rewindWizard}
style={this.state.currentStage == 0? {display: "none"}: {}
} />
<input type="button" value="Next" onClick={this.advanceWizard}
style={this.state.currentStage == wizardQuestions.length - 1?
{display: "none"}: {}
} />
<input type="button" value="Finish" onClick={this.seeResults}
style={this.state.currentStage < wizardQuestions.length - 1?
{display: "none"}: {}
} />
</div>
</div>
);
}
});
In your console you have this warning:
Each child in an array should have a unique "key" prop. Check the render method of App. See fb.me/react-warning-keys for more information.
If you don't, you're not using the development build: you should fix that.
React uses two things to determine if something is 'the same' between renders: the component class (e.g. QuestionnaireOption), and the key prop.
If either doesn't match the previous render, react considers it different, and the instance is recreated* and the subtree dom is discarded.
Assuming option.name can be used to determine equality, change your code to this:
var currentOptionNodes = currentWizardQuestion.options.map(function(option) {
var node = (
<QuestionnaireOption
name={option.name}
value={option.value}
key={option.name}
/>
);
return node;
});
For reference, reactid is an implementation detail, and may change or be removed at any time.
* if you just change the order of items, it'll try to just change the order for performance. There's currently a few cases where this doesn't happen, so it shouldn't be relied on.

What is workflow of the React

The code below is from React, which updates the DOM dynamically. I used the tutorial by Facebook react but did not understand the whole code, i.e which part of the code executes when and how it triggers the rest of the parts in the code. Please kindly help me in understanding the code.
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.renderComponent(<TodoApp />, mountNode);
The above code is used to dynamically update the DOM structure. This code is referred from http://facebook.github.io/react/ so please help in knowing the work process of the code.
Thanks, that's a very good question. Here's a rough overview of what is happening behind the scenes:
Initialization
It all starts with this line:
React.renderComponent(<TodoApp />, mountNode);
This instantiate the TodoApp component which calls:
TodoApp::getInitialState()
then, it renders the TodoApp component
TodoApp::render()
which in turns instantiate a TodoList
TodoList::render()
At this point, we have everything we need in order to render the initial markup
<div>
<h3>TODO</h3>
<ul></ul> <!-- <TodoList> -->
<form>
<input value="" />
<button>Add #1</button>
</form>
</div>
It is stringified and added inside of mountNode via innerHTML
OnChange
Then let's say you're going to enter some text in the input, then
TodoApp::onChange
is going to be called, which is going to call
TodoApp::setState
and in turn will call
TodoApp::render
again and generate the updated DOM
<div>
<h3>TODO</h3>
<ul></ul> <!-- <TodoList> -->
<form>
<input value="sometext" />
<button>Add #1</button>
</form>
</div>
What's happening at this point is that React is going to do a diff between the previous DOM and the current one.
<div>
<input
- value=""
+ value="sometext"
Only the value of the input changed, so React is going to just update this particular attribute in the real DOM.
You can find more general explanation on React official page.
Generally the react lifecycle can be described by the following stages (which can repeat multiple times once the components is created):
Initializing values (only once):
constructor(){ ... }
Mounting, if you need to add something after initial rendering (only once):
componentDidMount(){...}
Re-rendering functions, variables and components
myArrowFunction = () => {
...
this.setState({...})
...
}
Updating:
componentDidUpdate()}{...}
shouldComponentUpdate(){...}
Unmounting:
componentWillUnmount(){...}
Rendering happens here
render(){...}

How to properly bind text box value back to model in a observable array?

I have a model that has an observable array, I can display the data in a text box, but I can't figure out how to bind it back to the original array.
Here is the working sample I have.
<ul data-bind='foreach: frameworks'>
<li>
<button class='btn' value='pick me'
data-bind='text: name, click: $parent.selectFramework'>
</button>
</li>
</ul>
<input type='text' data-bind='value: selectedFramework().name' />
<pre data-bind='text: ko.toJSON($root.selectedFramework, null, 4)'>
</pre>
var Framework = {
name: ''
};
var App = new function () {
var self = this;
self.frameworks = ko.observableArray();
self.selectFramework = function (item) {
self.selectedFramework(item);
};
self.selectedFramework = ko.observable(Framework);
};
App.frameworks([{name: 'foo'}, {name: 'bar'}]);
ko.applyBindings(App);
You are almost there. You need to make the 'name' properties on each framework observable. I have updated your JsFiddle here
App.frameworks([{
name: ko.observable('foo')
}, {
name: ko.observable('bar')
}]);
The value is only stored in your selectedFramework observable, so you would be able to access it via App.selectedFramework(). The observable doesn't take any variable and make it observable, it will store whatever value you pass it internally. If you want to update the external Framework variable, you would do that in your selectFramework function.
self.selectFramework = function (item) {
self.selectedFramework(item);
Framework = item;
};

Categories

Resources