This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 7 years ago.
Good day. I have the following problem: I have a class calculator and it changes state of firstOperand parameter on button click. But state doesn't change immediately, it changes the state of firstOperand only when I clicked one more button. Result is delay in 1 operation: I clicked 1 - indicator doesn't changed, I clicked 2 - indicator changed to 1, I clicked 5 - indicator changed to 12, etc.
Here is part of my code:
var Indicator = React.createClass({
render: function () {
return (
<div className="calc-indicator">
<input id="indicator" type="text" maxLength="20" size="20" value={this.props.value}/>
</div>
);
}
});
var Button = React.createClass({
render: function () {
return (
<div className="nav-button">
<button type="button" id={this.props.identifier} onClick={this.props.clickHandler}>
{this.props.digit}
</button>
</div>
);
}
});
var Calculator = React.createClass({
setFirstOperand: function (value) {
this.setState(
{
firstOperand: value
}
);
if (isDebug) {
console.log("First operand state is set to " + value + ".");
}
},
setSecondOperand: function (value) {
this.setState(
{
secondOperand: value
}
);
},
changeIndicator: function (value) {
if (isDebug) {
console.log("///Change indicator method is working///");
console.log("change indicator parameter is " + value);
console.log("first operand is " + this.state.firstOperand);
console.log("second operand is " + this.state.secondOperand);
console.log("operation is " + this.state.operation);
}
if (!value) { // if value hasn't been gotten
value = '';
if (this.state.firstOperand) {
value += this.state.firstOperand;
} else {
value = '0';
}
if (this.state.operation) {
value += this.state.operation;
}
if (this.state.secondOperand) {
value += this.state.secondOperand;
}
}
this.setState(
{
value: value
}
);
if (isDebug) {
console.log("indicator is changed to " + value);
console.log("/////////////////////////////////////");
}
},
getInitialState: function () {
calculator = this;
return {
firstOperand: null,
secondOperand: null,
operation: null,
divisionByZero: false,
value: '0'
};
},
cipherClicked: function (event) {
if (this.state.divisionByZero) {
this.setDivisionByZero(false);
}
var newValue = event.target.innerHTML;
var firstValue = this.state.firstOperand;
var secondValue = this.state.secondOperand;
var operation = this.state.operation;
if (operation) {
if (secondValue) {
secondValue = secondValue + newValue;
} else {
secondValue = newValue;
}
if (isDebug) {
console.log("Second operand state is setting to " + secondValue + ".");
}
this.setSecondOperand(secondValue);
} else {
if (firstValue) {
firstValue = firstValue + newValue;
} else {
if (newValue != 0) { // if indicator contains 0 and you have tried to add one 0 more
firstValue = newValue;
}
}
if (isDebug) {
console.log("First operand state is setting to " + firstValue + ".");
}
this.setFirstOperand(firstValue);
setOperand(firstValue);
}
if (isDebug) {
console.log("Calling changeIndicator function.");
}
this.changeIndicator();
},
render: function () {
return (
<div id="calculator">
<table>
<tr>
<td colSpan="4">
<Indicator value={this.state.value} />
</td>
</tr>
<tr>
<td>
<Button identifier="MC" digit="MC" clickHandler={this.memoryNavigationClicked}/>
</td>
<td>
<Button identifier="MR" digit="MR" clickHandler={this.memoryNavigationClicked}/>
</td>
<td>
<Button identifier="M+" digit="M+" clickHandler={this.memoryNavigationClicked}/>
</td>
<td>
<Button identifier="M" digit="M" clickHandler={this.memoryNavigationClicked}/>
</td>
</tr>
<tr>
<td>
<Button identifier="BS" digit="BS" clickHandler={this.editNavigationClicked}/>
</td>
<td>
<Button identifier="CL" digit="CL" clickHandler={this.editNavigationClicked}/>
</td>
<td>
<Button identifier="C" digit="C" clickHandler={this.editNavigationClicked}/>
</td>
<td>
<Button identifier="negate" digit="+-" clickHandler={this.negateClicked}/>
</td>
</tr>
<tr>
<td>
<Button identifier="7" digit="7" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="8" digit="8" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="9" digit="9" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="divide" digit="/" clickHandler={this.operationClicked}/>
</td>
</tr>
<tr>
<td>
<Button identifier="4" digit="4" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="5" digit="5" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="6" digit="6" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="multiply" digit="*" clickHandler={this.operationClicked}/>
</td>
</tr>
<tr>
<td>
<Button identifier="1" digit="1" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="2" digit="2" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="3" digit="3" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="minus" digit="-" clickHandler={this.operationClicked}/>
</td>
</tr>
<tr>
<td>
<Button identifier="0" digit="0" clickHandler={this.cipherClicked}/>
</td>
<td>
<Button identifier="dot" digit="." clickHandler={this.dotClicked}/>
</td>
<td>
<Button identifier="eq" digit="=" clickHandler={this.operationClicked}/>
</td>
<td>
<Button identifier="plus" digit="+" clickHandler={this.operationClicked}/>
</td>
</tr>
</table>
</div>
);
}
});
React.render(<Calculator />, document.body);
</script>
</body>
</html>
As per my React Native answer here, setState can be an asynchronous operation, not synchronous. This means that updates to state could be batched together and not done immediately in order to get a performance boost. If you really need to do something after state has been truly updated, there's a callback parameter:
this.setState({ searchString: event.nativeEvent.text }, function(newState) {
console.log('Changed State');
console.log('searchString = ' + this.state.searchString);
}.bind(this));
It’s also mentioned in the React Documentation.
Related
In the code below I can sort the table only in ascending order. When I click the table header a second time it does not switch the order to descending order.
Can anyone help me how sort the table column in both directions? Is there any alternative way available to sort the textbox value that is inside the table data?
$("#sortcol").click(function() {
const table = document.getElementById('tab_logic');
const headers = table.querySelectorAll('th');
const tableBody = table.querySelector('tbody');
const rows = tableBody.querySelectorAll('tr');
const directions = Array.from(headers).map(function(header) {
return '';
});
const transform = function(index, content) {
const type = headers[index].getAttribute('data-type');
switch (type) {
case 'number':
return parseFloat(content);
case 'string':
default:
return content;
}
};
const sortColumn = function(index) {
const direction = directions[index] || 'asc';
const multiplier = direction === 'asc' ? 1 : -1;
const newRows = Array.from(rows);
newRows.sort(function(rowA, rowB) {
const cellA = rowA.getElementsByTagName("td")[index];
const cellB = rowB.getElementsByTagName("td")[index];
const cellC = $(cellA).find('input').val();
const cellD = $(cellB).find('input').val();
const a = transform(index, cellC);
const b = transform(index, cellD);
switch (true) {
case a > b:
return 1 * multiplier;
case a < b:
return -1 * multiplier;
case a === b:
return 0;
}
});
[].forEach.call(rows, function(row) {
tableBody.removeChild(row);
});
directions[index] = direction === 'asc' ? 'desc' : 'asc';
newRows.forEach(function(newRow) {
tableBody.appendChild(newRow);
});
};
sortColumn(1);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="tab_logic" style="width: 40%; border: 1px solid black;">
<thead>
<tr>
<th data-type="number">check</th>
<th id="sortcol">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" MySQL">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Python">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Javascript">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Angular JS">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Csharp">
</td>
</tr>
</tbody>
</table>
your code is perfectly fine only problem is when you are clicking on it direction variable is resetting and 'asc' value is not persisting into an array. please check the below code.
var rows;
var directions;
var headers;
var tableBody;
$(document).ready(function(){
const table = document.getElementById('tab_logic');
headers = table.querySelectorAll('th');
tableBody = table.querySelector('tbody');
rows = tableBody.querySelectorAll('tr');
directions = Array.from(headers).map(function(header) {
return '';
});
})
const transform = function(index, content) {
const type = headers[index].getAttribute('data-type');
switch (type) {
case 'number':
return parseFloat(content);
case 'string':
default:
return content;
}
};
const sortColumn = function(index) {
const direction = directions[index] || 'asc';
const multiplier = direction === 'asc' ? 1 : -1;
const newRows = Array.from(rows);
newRows.sort(function(rowA, rowB) {
const cellA = rowA.getElementsByTagName("td")[index];
const cellB = rowB.getElementsByTagName("td")[index];
const cellC = $(cellA).find('input').val();
const cellD = $(cellB).find('input').val();
const a = transform(index, cellC);
const b = transform(index, cellD);
switch (true) {
case a > b:
return 1 * multiplier;
case a < b:
return -1 * multiplier;
case a === b:
return 0;
}
});
[].forEach.call(rows, function(row) {
tableBody.removeChild(row);
});
newRows.forEach(function(newRow) {
tableBody.appendChild(newRow);
});
directions[index] = direction === 'asc' ? 'desc' : 'asc';
};
$("#sortcol").click(function() {
sortColumn(1);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="tab_logic" style="width: 40%; border: 1px solid black;">
<thead>
<tr>
<th data-type="number">check</th>
<th id="sortcol">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" MySQL">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Python">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Javascript">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Angular JS">
</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>
<input type="text" Value=" Csharp">
</td>
</tr>
</tbody>
</table>
I am trying to delete a certain element from an array, that I display in a HTML-Table.
Here's the frontend:
HTML-table_frontend
I push the Data from the input into an array userData.
The HTML-Table is build within a v-forloop in VUEjs.
Upon Pressing a button, I want the dedicated function (e.g. deleteData()) to only be applied to the row that that button is in.
How can I tell the button what Array-Index it needs to target?
Here's the code snippets:
Table:
<table id="userData">
<tr>
<th>Name</th>
<th>Email Address</th>
<th>Mobile Number</th>
<th>Action</th>
</tr>
<tr v-for="(userData, k) in userData" :key="k">
<td class="name">
<input readonly class="tableDisplay" type="text" v-model="userData.name" />
</td>
<td>
<input readonly class="tableDisplay" type="email" v-model="userData.email" />
</td>
<td>
<input readonly class="tableDisplay" type="number" v-model="userData.number" />
</td>
<td>
<button type='button' class="buttonRead" #click="readData" >Read</button>
<button type='button' class="buttonEdit" #click="editData">Edit</button>
<button type='button' class="buttonDelete" #click="deleteData">Delete</button>
</td>
</tr>
</table>
Javascript in the .vue:
<script>
export default {
data() {
return{
userData:[],
newUser: {
name: '',
email:'',
number:''
}
};
},
methods: {
customSubmit(){
this.userData.push(this.newUser)
this.newUser = {
name:'',
email:'',
number:''
}
console.log(this.userData)
},
}
}
</script>
example with passing index
<button type='button' class="buttonDelete" #click="deleteData(k)">Delete</button>
methods: {
deleteData(k) {
this.userData.splice(k, 1)
}
}
or passing object
<button type='button' class="buttonDelete" #click="deleteData(userData)">Delete</button>
methods: {
deleteData(userData) {
let index = this.userData.indexOf(userData)
this.userData.splice(index, 1)
}
}
UPD. readData
<button type='button' class="buttonRead" #click="readData(k)" >Read</button>
methods: {
readData (k) {
console.log(this.userData[k].name)
},
}
also change userData variable name
<tr v-for="(rowUser, k) in userData" :key="k">
in inputs
<input ... v-model="rowUser.name" />
live example https://jsfiddle.net/8v2cx4je/
I am trying to create a table that places all the answers that the user has answered by buying a ticket under the correct questions, and if they haven't answered then show the dash symbol.
The problem is that if there are more than on tickets that the user has purchased then the answers get all messed up.
Example:
So, for this the number should go under 'Mobile Num?' in the same row as 'Free 2' and 'Male', 'Aadil' and '20' should go under 'Gender?', 'Name?' and 'Age?' in the same row as Free.
This is my HTML:
<template>
<fragment>
<tr>
<td :rowspan="countArray + 1">
<img :src="user.profile_image">
</td>
<td :rowspan="countArray + 1">
{{user.first_name + " " + user.last_name}}
</td>
<td :rowspan="countArray + 1">
<div v-for="(nameTick, nameKey) in name" :key="nameKey" class="tdStyle">
<td>
{{nameTick}}
</td>
</div>
</td>
</tr>
<tr v-for="(ticketQues, quesKey) in ticketAns" :key="quesKey">
<td v-for="(ans, ansKey) in ticketQues.questions" :key="ansKey">
{{ans.answer}}
</td>
</tr>
</fragment>
</template>
This is my JS:
beforeMount: function() {
this.$axios.$get(`/user/${this.userId}`).then(response => {
this.user = response
});
for (let i = 0; i < this.ticketName.tickets.length; i++) {
this.tickets_name = this.ticketName.tickets[i].ticket_name;
this.countArray = this.ticketName.tickets[i].count;
this.name.push(this.tickets_name);
};
},
watch: {
tickets: {
handler: function(val) {
for (let x = 0; x < val.length; x++) {
this.$axios.$get(`/ticket/${val[x].id}/answer`).then(response => {
for (let i = 0; i < response.questions.length; i++) {
this.userAnswered = response.questions[i];
this.answered.push(this.userAnswered.answer);
}
console.log(response)
this.allQuestions = this.ticketAns.push(response);
})
}
// this.userAnswers.push(this.ticketAns);
this.userAnswers.push(this.answered);
}
}
}
If someone can help it would be much appreciated.
Remove the <td> inside <td>, after that use a <div> with grids and css to organize inside the <div>.
<template>
<fragment>
<tr>
<td :rowspan="countArray + 1">
<img :src="user.profile_image">
</td>
<td :rowspan="countArray + 1">
{{user.first_name + " " + user.last_name}}
</td>
<td :rowspan="countArray + 1">
<div v-for="(nameTick, nameKey) in name" :key="nameKey" class="tdStyle">
<div class="ui grid">
{{nameTick}}
</div>
</div>
</td>
</tr>
<tr v-for="(ticketQues, quesKey) in ticketAns" :key="quesKey">
<td v-for="(ans, ansKey) in ticketQues.questions" :key="ansKey">
{{ans.answer}}
</td>
</tr>
</fragment>
</template>
I have this simple chrome extension that allows the user to create a table however I can't bind the remove Button.
options.html
<body>
<!-- would be created as the user clicks on new Row -->
<table id="rows">
<tr id="row0">
<td>
<input id="textBox" type="text">
<button id="remove">Remove</button>
</td>
</tr>
</table>
<!-- used as template -->
<table hidden>
<tbody>
<tr id="rowTemplate">
<td>
<input id="textBox" type="text">
<button id="remove">Remove</button>
</td>
</tr>
</tbody>
</table>
<button id="newRow">new Row</button>
<script src="options.js"></script>
options.js
function Row() {
var rows = document.getElementById('rows');
this.node = document.getElementById('rowTemplate').cloneNode(true);
this.node.id = 'row' + (Row.next_id++);
this.node.row = this;
rows.appendChild(this.node);
this.node.hidden = false;
var row = this;
this.getElement('remove').onclick = function () {
row.node.parentNode.removeChild(row.node);
}
}
Row.next_id = 0;
Row.prototype.getElement = function (name) {
return document.querySelector('#' + this.node.id + ' .' + name);
}
window.onload = function() {
document.getElementById('newRow').onclick = function() {
new Row();
};
}
The problem lies in the querySelector because whenever I click on 'new Row', I get an error Uncaught TypeError: Cannot set property 'onclick' of null pointing to the getElement('remove').onclick which calls getElement() where the querySelector returns null.
Im having problems with my calculator. The buttons work and all align right but I can't get anything to show up on the monitor box or calculate anything. I listed my code below can anyone help me find where I went wrong? I feel it has to do with the true or false but I can't figure it out.
Here is the HTML code:
<body>
<table>
<tr>
<input id="display" type="text" value="0"/><span id="currOp"></span>
<tr>
<td>
<button id="7" class="num">7</button>
</td>
<td>
<button id="8" class="num">8</button>
</td>
<td>
<button id="9" class="num">9</button>
</td>
<td>
<button id="plus" class="operator">+</button>
</td>
<td>
<button id="clear">C</button>
</td>
</tr>
<tr>
<td>
<button id="4" class="num">4</button>
</td>
<td>
<button id="5" class="num">5</button>
</td>
<td>
<button id="6" class="num">6</button>
</td>
<td>
<button id="minus" class="operator">-</button>
</td>
<td>
<button id="root">√</button>
</td>
</tr>
<tr>
<td>
<button id="1" class="num">1</button>
</td>
<td>
<button id="2" class="num">2</button>
</td>
<td>
<button id="3" class="num">3</button>
</td>
<td>
<button id="mult" class="operator">x</button>
</td>
<td>
<button id="power" class="operator">x^y</button>
</td>
</tr>
<tr>
<td>
<button id="0" class="num">0</button>
</td>
<td>
<button id="decimal">.</button>
</td>
<td>
<button id="invert">±</button>
</td>
<td>
<button id="divid" class="operator">÷</button>
</td>
<td>
<button id="equals">=</button>
</td>
</tr>
Here is the CSS code:
button {
height: 40px;
width: 40px;
font-size: 110%;
}
#display{
font-size: 120%;
text-align: right;
}
span {
font-size: 150%;
}
and here is the javascript code:
var isOperating = true;
var isfloating = false;
var toBeCleared = true;
var operator;
var operand;
var display;
$(document).ready(init);
function init() {
display = $('#display');
$('.num').on('click', numClicked);
$('.operator').on('click', operatorClicked);
$('#invert').on('click', invertClicked);
$('#root').on('click', rootClicked);
$('#decimal').on('click', decimalClicked);
$('#equals').on('click', equalsClicked);
$('#clear').on('click', clearClicked);
}
function numClicked() {
var currVal = display.val();
var clickedNum = $(this).text();
if (currVal === "0" || toBeCleared) {
toBeCleared = true;
display.val(clickedNum);
} else {
display.val(currVal + clickedNum);
}
}
function invertClicked() {
display.val(display.val() * -1);
}
function rootClicked() {
display.val(Math.sqrt(evaluate()));
}
function decimalClicked() {
if (toBeCleared) {
display.val('0.');
toBeCleared = true;
} else {
if (!isFloating) {
display.val(display.val().concat('.'));
}
}
isFloating = false;
}
function equalsClicked() {
display.val(evaluate());
reset();
}
function clearClicked() {
reset();
display.val('0');
}
function reset() {
toBeCleared = true;
isOperating = true;
isFloating = false;
operator = null;
operand = null;
$('#currOp').text('');
}
function operatorClicked() {
if (isOperating) {
display.val(evaluate());
}
switch ($(this).attr('id')) {
case 'plus': operator = '+'; break;
case 'minus': operator = '-'; break;
case 'mult': operator = 'x'; break;
case 'divide': operator = '÷'; break;
case 'power': operator = '^'; break;
}
operand = parseFloat(display.val());
isOperating = true;
toBeCleared = true;
$('#currOp').text(operator);
}
function evaluate() {
`enter code here` var currVal = parseFloat(display.val());
var result;
switch (operator) {
case '+': result = operand + currVal; break;
case '-': result = operand - currVal; break;
case 'x': result = operand * currVal; break;
case '÷':
if (currVal === 0) {
result = 'Err';
} else {
result = operand / currVal;
}
break;
case '^': result = Math.pow(operand, currVal); break;
default: result = currVal;
}
return result;
}
You should set toBeCleared = false;
if (currVal === "0" || toBeCleared) {
toBeCleared = false;
display.val(clickedNum);
} else {
display.val(currVal + clickedNum);
}