My program generates few inputs and i try to push data to my state's array's
export default class TransmittersTable extends Component {
constructor() {
super()
this.state = {
axisX: [],
axisY:[],
power: [],
}
}
updateAxisX(e) {
this.setState({
axisX: this.state.axisX.push(e.target.value)
})
}
updateAxisY(e) {
this.setState({
axisY: this.state.axisY.push(e.target.value)
})
}
updateAxisPower(e) {
this.setState({
power: this.state.power.push(e.target.value)
})
}
generateTransmittersItems(){
let transmitters = [];
for(let i = 0; i < this.props.numberOfTransmitters; i++) {
transmitters.push(
<tr>
<td><input id={i} ref="axisX" type="text" onChange={this.updateAxisX.bind(this)}/></td>
<td><input id={i} ref="axisY" type="text" onChange={this.updateAxisY.bind(this)}/></td>
<td><input id={i} ref="power" type="text" onChange={this.updateAxisPower.bind(this)}/></td>
</tr>
);
}
return transmitters
}
componentWillMound(){}
render() {
return (
<table>
<thead>
<tr>
<th>Axis X</th>
<th>Axis Y</th>
<th>Power</th>
</tr>
</thead>
<tbody>
{this.generateTransmittersItems()}
</tbody>
</table>
)
}
}
In first row of inputs evrything is okay but if i try to push another value form another row of input's to the same state array (ex. axisX) my console send me this error "this.state.axisX.push is not a function".
What i did wrong and what i have to do to push more values to the same array from input using the same function?
I think the problem isn't related to the react state issue.
When you used the "push" methods, it won't return an array but return the length of the array, and that is the reason why when you use "push" method in second time will get the error "this.state.axisX.push is not a function".
So, if you need to change your state, you can just use "concat" method like this to get a new array as return:
this.setState({
axisX: this.state.axisX.concat([e.target.value])
})
var a = ["Hi", "There"];
var b = a.push("Oh");
console.log(b); // get 3, not an array
console.log(a); // ["Hi", "There", "Oh"]
Related
I'm trying to figure out how to remove a row from a simple table using a simple button.
I try to use the index but with what I wrote, when I click on the line to delete, it is only all the others that are deleted ...
I guess the problem comes from the way I use the index but I have some difficulties to understand the behavior.
let users = [
{ firstName: "John", lastName: "Connor", age: 20 },
{ firstName: "Geralt", lastName: "Rivia", age: 45 },
{ firstName: "Nathan", lastName: "Drake", age: 36 }
]
class exercice extends React.Component {
constructor(props){
super(props);
this.state = {
users: users
}
}
onClickDeleteRow = index => {
users = users.splice(index,1)
this.setState({users: users})
console.log(this.state.users)
}
render() {
let usersName = this.state.users.map((user, index) => (
<tr key={index}>
<td>{user.firstName} </td>
<td>{user.lastName} </td>
<td><button onClick={() => this.onClickDeleteRow(index)}>Delete</button></td>
</tr>
))
return (
<table>
<thead>
<tr>
<th> firstName </th>
<th> lastName</th>
</tr>
</thead>
<tbody>
{usersName}
</tbody>
</table>
)
}
}
ReactDOM.render(
<div className="firstContainer">
<Exercice />
</div>,
document.getElementById('root')
)
.splice() method returns an array containing the elements that were removed from the original array on which .splice() method was called. So if index is 1, then
users = users.splice(index,1)
will update users and assign an array containing one element to the users constant. If index is 1 then after the above statement, users will be
users = [{ firstName: "Geralt", lastName: "Rivia", age: 45 }]
which is what gets set in the state. That is why all other rows are removed except the one that you wanted to delete.
Solution
You could solve the problem in couple of ways:
Change
users = users.splice(index, 1);
to
users.splice(index, 1);
this will make sure that you don't update the users with the return value of .splice() method.
Problem with the first approach is that it modifies the original users array. You could avoid modifying the users array by using the .filter() method.
Change the onClickDeleteRow method as shown below:
onClickDeleteRow = (index) => {
const updatedUsers = this.state.users.filter((user, idx) => idx !== index);
this.setState({ users: updatedUsers });
};
I think you should call this.state.users in the method
onClickDeleteRow = index => {
const users = this.state.users;
users.splice(index, 1);
this.setState({ users: users });
};
I have started learning ReactJS and I am stuck on this error for a while now.
export default class Bag extends Component {
constructor(props){
super(props)
this.state = {
books : [
{
name : "Origin",
read : "Yes",
owner: "Yes"
},
{
name: "The Alchemist",
read: "Yes",
owner: "Yes"
},
{
name : "The Tale of Twisted Candle",
read : "Yes",
owner: "Yes"
},
{
name: "Martian",
read: "Yes",
owner: "Yes"
}
]
}
this.setStateHandler = this.setStateHandler.bind(this)
this.handleChange = this.handleChange.bind(this)
}
setStateHandler(){
this.setState({books: this.state.books })
}
handleChange(book){
console.log("Here is your book:")
console.log(book)
console.log(this.state.books)
let tempVal = {
name : book.name,
read : book.read,
owner: book.owner
}
this.setState({books: this.state.books.push(tempVal) })
console.log(this.state.books)
}
render(){
let b = this.state.books
return (
<div align="center">
<h1>{this.props.name}'s Bag</h1>
<table>
<tbody>
<tr>
<th>Book Name</th>
<th>Read</th>
<th>Ownership</th>
</tr>
</tbody>
<tbody>
{ b.map(function(book){
return <Book name={book.name} read={book.read} owner={book.owner}/>
})}
</tbody>
</table>
<BookForm name="Book" read="No" owner="No" onChange={this.handleChange} />
<Button />
</div>
)
}
}
When code is run for first time, everything works fine. But when I try to submit a new book, an error is thrown.
TypeError: b.map is not a function
While looking for the solution in other similar questions, they all referred that the map function is for Array not Object. So, I have checked that too. Apparently, the new value of 'b' after submit is still an Array.
The line:
this.setState({books: this.state.books.push(tempVal) })
in your handleChange method is likely the problem. The push method returns a number, so that line is actually setting the value of books in your state to a number, not an array. You might try changing it to:
this.setState({books: this.state.books.concat([tempVal]) })
Or, if you want to use the functional setState style:
this.setState(prevState => {
prevState.books.push(tempVal);
return prevState;
});
I have small ReactJS chat application, messages are stored inside global array client.chat as object with propery txt, which contains text. The problem is perfomance, on ~2K messages I have to wait for few seconds after each new message even if networking is commented out, so its simply rerendeing of HTML. I tried to implement same app in AngularJS and it has no delay at all. So where is the bottleneck here?
var client = {user: {}, chat: []};
class App extends React.Component {
constructor() {
super();
this.socketio = ...
// here i perform some network initialization and
// call client.updateChat when data has arrived over network
client.updateChat = function () {
me.setState({chat: client.chat.concat([]).reverse()});
};
client.addMessage = function (msg) {
me.setState(prevState=>({
chat:[msg].concat(prevState.chat)
}));
};
this.updateState = this.updateState.bind(this);
this.state = {chat: []};
}
updateState(e) {
this.setState({data: e.target.value});
}
render() {
return (
<span>
<Input socketio={this.socketio} visitorId={this.visitorId}/>
<table>
<Chat data={this.state.chat} socketio={this.socketio}>
</Chat>
</table>
</span>
);
}
}
This is Input component, representing essentially input box:
class Input extends React.Component {
constructor() {
super();
this.state = {inputValue: ''};
this.updateInputValue = this.updateInputValue.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.getFile = this.getFile.bind(this);
}
getFile(e) {/* attachment handling */ }
handleKeyPress(target) {
if (target.charCode == 13) {
if (this.state.inputValue.length == 0) return;
var inputValue = this.state.inputValue;
this.setState({inputValue: ''});
var ts = Date.now();
var elem = {
txt: inputValue, file: null, ts: ts, from: 'support', tsStr: formateDate(ts), name: client.user.name, attachmentName: null, dataType: null
};
client.addMessage(elem);
}
}
updateInputValue(evt) {
this.setState({inputValue: evt.target.value});
}
render() {
return (<div className="input">
<table width="100%">
<tbody>
<tr>
<td>
<label className="customUpload btnUpload btnM">
<span><img width="15px" src="images/attach.png"/></span>
<input onChange={this.getFile} id="fileUpload" type="file" className="upload"/>
</label>
</td>
<td>
<input value={this.state.inputValue}
onKeyPress={this.handleKeyPress}
onChange={this.updateInputValue}
className="textInput"/>
</td>
</tr>
</tbody>
</table>
</div>);
}
}
This is chat component, representing chat itself.
class Chat extends React.Component {
constructor() {super();}
render() {
return (
<tbody>
{this.props.data.map((p, i) => <Msg socketio={this.props.socketio} key={i} data={p}/>)}
</tbody>
);
}
}
This is a single message:
class Msg extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.data.txt == nextProps.data.txt) {
return false;
}
return true;
}
render() {
var words = (this.props.data.txt || "").split(" ");
// this piece of code splits this.props.data.txt by spaces
// and converts it to array of words warr
// ...
return (
<tr className={this.trStyle()}>
<td>
<div className="msg">
{
warr.map((word, k) => <Word socketio={this.props.socketio} key={k} data={word.txt}/>)
}
</div>
</td>
</tr>
);
}
}
This class is for a single word inside a message. If a word is too long, function shortened() returns abbreviated version.
class Word extends React.Component {
shortened() { //....
render() {
return (
<span className={this.wordClass()} onClick={this.click}>{this.shortened()} </span>
);
}
}
I have implemented adding new messages using concat() instead of push(), following perfomance guidelines from Facebook and also implemented shouldComponentUpdate to avoid txt.split(" ") recalculation. But it didn't give me any perfomance boost. Can anybody give me some more advice or idea?
What is the size of response object which your are binding with react? we had similar issue,we had earlier 120kb size of json object which we were binding with react but later on optimize the json object and devided the json object in two part. 1. Pageload JSON object -It was having complete Json object with all fields and values(default)
2. Delta object- It was having only changed object field and it's value which we use to merge in React
I am building a React.js application where I want to allow users to input styles in a text area which will affect the style of another element.
First, I have a row component which looks like this:
function Row(props) {
return (
<tr>
<td width="50%">
<textarea
style={{}}
value={props.style}
onChange={props.onStyleChange}>
</textarea>
</td>
<td><div className="">Row {props.id+1}</div></td>
</tr>
)
};
I am iterating through a list of rowData to populate my rows, which can be found here:
{this.state.rowData.map(function(row, index) {
return (
<Row
id={row.id}
style={row.style}
onStyleChange={function(e) {this.onStyleChange(e, row.id)}.bind(this)}/>
);
}.bind(this))}
Then in my onStyleChange function I have:
onStyleChange: function(e, id) {
this.state.rowData[id].style = e.target.value;
this.setState(this.state);
}
So as a user enters data into the the textarea, it adds to the i'th element in my rowData array. Assuming its the 0th row and the user enters "Hello" into the text area, then rowData[0].style = "Hello".
However, I would like to be able to do something like this: style={{props.style}} inside my Row component. But because it is currently a string it does not work. I have also tried style={JSON.parse(props.style)} which throws an error every time I add a new row because props.style='{}'. The error reads Uncaught SyntaxError: Unexpected token f in JSON at position 1
Always grateful for any help. There's got to be a much easier way to do this. Thank you.
Two steps to convert inline-style toobject-style` as restricted by React :
Parse the string to be a JSON object.
Convert the keys of this object to camel case (z-index becomes zIndex.. so on)
Congrats! i wrote the algorithm , check below :
const example1= "position:absolute;h-index:9001;"
const example2 = "-ms-transform: rotate(20deg);-webkit-transform: rotate(20deg);";
// 2ⁿᵈ step logic
const camelize = (string) => string.replace(/-([a-z])/gi,(s, group) => group.toUpperCase());
// 1ˢᵗ step logic which calls the 2ⁿᵈ step logic
const style2object = (style) => style.split(';').filter(s => s.length)
.reduce((a, b) => {
const keyValue = b.split(':');
a[camelize(keyValue[0])] = keyValue[1] ;
return a;
} ,{});
console.log("Example 1 : ", example1, '\n',
style2object(example1)
)
console.log("Example 2 : ", example2, '\n',
style2object(example2)
)
If it is helpful the style attribute needs an object like {"color": "blue"}
I made a demo with your code the only thing that escapes me is how to update with the onChange event.
function Row(props) {
const styleValue = JSON.stringify(props.style);
return (
<tr>
<td width="50%">
<textarea
style={props.style}
defaultValue={styleValue}
onChange={props.onStyleChange}/>
</td>
<td><div className="">Row {props.id+1}</div></td>
</tr>
)
};
class App extends React.Component {
state = {
rowData: [{
id: 1,
style: {
color: 'blue'
}
}, {
id: 2,
style: {
color: 'red',
backgroundColor:'#000'
}
}]
};
onStyleChange(e, id) {
const rows = this.state.rowData;
const row = rows.find(row => row.id === id);
const index = rows.indexOf(row);
rows[index]['style'] = JSON.parse(e.target.value);
this.setState({
rowData: rows
});
}
render() {
return (
<table>
<tbody>
{
this.state.rowData.map(function(row, index) {
return (
<Row
id={row.id}
key={index}
style={row.style}
onStyleChange={function(e) {this.onStyleChange(e, row.id)}.bind(this)}/>
);
}.bind(this))
}
</tbody>
</table>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
http://codepen.io/GGarciaSeco/pen/vgVEGX?editors=0010
You can take a look to the React documentation in the next link
https://facebook.github.io/react/docs/dom-elements.html#style
I'm trying to create a table from a JSON response formulated from a submitted form, therefore the initial render needs to be blank, but this blank state is proving to be an issue.
The issue is complicated further by the fact that the response could have different headers, number of columns, and order.
Parent component
This gets the resultData and passes it to a child component
<ReportsTable title={this.props.title} resultData={this.state.resultData} />
Child component
var ReportsTable = React.createClass({
render: function() {
var resultData = this.props.resultData;
return(
<div>
<h3>{this.props.title}</h3>
<table>
<tr>
//iteration here
</tr>
</table>
</div>
)
}
});
Any attempt at iteration gives me a
Uncaught TypeError: Cannot read property XXX of undefined
The Data received is in this type of format
[Array[1], Array[1]]
0: Array[1]
0: Object
family_name: "Sales"
gross_margin: "0%"
net_profit: "$0.00"
profit_percent: "0%"
quantity_on_hand: 2863
retail: "$9,347.12"
total_cost: "$7,615.96"
total_sold: 49
1: Array[1]
0: Object
family_name: "Service"
gross_margin: "0%"
net_profit: "$0.00"
profit_percent: "0%"
quantity_on_hand: 147.5
retail: "$939.05"
total_cost: "$268.40"
total_sold: 10.8
[UPDATE]
So we modified the response from the server so that I get one less nest in the Array. But now when I try
resultData.map(function(item) { })
and I get an "Uncaught TypeError: undefined is not a function" error as I'm trying to map through the properties of the Object. When I try to map through an Array it works so I don't think it's my syntax.
In the end, my trouble is iterating through the properties of each Object.
This part from the parent works
{resultData.map(function(tableRow, i) {
return (
<TableRow tableRow={tableRow} key={i} />
);
})}
This part in the Child Component does not
var TableRow = React.createClass({
render: function(){
var tableRow = this.props.tableRow;
console.log(tableRow);
return(
<tr key={tableRow}>
{tableRow.map(function(tableItem, i){
<td key={i}>{tableItem}</td>
})}
</tr>
);
}
});
I have had the same problem.
The reason why i got "Uncaught TypeError: undefined is not a function" was because i tried to iterate over the properties of a json object using map which is not possible. The solution for me was to iterate over the keys of the json object using Object.keys(). See below for my solution.
Data:
{
"status": {
"build": {
"a":"b",
"c":"d"
}
}
}
`render: function(){
var buildInfo = this.props.applicationInfo.status.build;
var properties = Object.keys(buildInfo).map((k, idx) => {
return (
<p key={idx}><strong>{k}</strong> - {buildInfo[k]}</p>
);
});
return(
<div>
{properties}
</div>
);
}`
If you have JSON instead of Array and you want to loop in JSX react render function use Object.keys:
<select className="form-control" >
{Object.keys(item.unit).map(unit => {
return <option value="1">1</option>
})}
</select>
So this works
<table className="table table-condensed table-striped">
<thead>
<tr>
{resultTitles.map(function(title){
var textAlignLeft = false;
if(textLeftMatch.test(title)){
textAlignLeft = true;
}
title = title.replace(/_/g, " ");
return <th key={title} className={textAlignLeft ? 'text-left' : ''}>{title}</th>
})}
</tr>
</thead>
<tbody>
{resultData.map(function(tableRow, i) {
return (
<TableRow tableRow={tableRow} key={i} />
);
})}
</tbody>
</table>
var TableRow = React.createClass({
render: function(){
var tableRow = this.props.tableRow;
var rowArray = $.map(tableRow, function(value, index){
return [value];
});
return(
<tr key={tableRow}>
{rowArray.map(function(tableItem, i){
return <td key={i} className={(i === 0) ? 'text-left' : ''}>{tableItem}</td>
})}
</tr>
);
}
});
However, after searching for awhile, I found a better starting point found here http://dynamictyped.github.io/Griddle/quickstart.html