React.js, How to track previous values while rendering? - javascript

new to Reactjs, so I'm trying to understand what concepts I'm missing.
I have an array of values, say [1,1,1,2,2,2,3,3,3] that I would like to process it as: "1...2...3...", but I have no idea how to keep track of what the previous value that was rendered.
My first thought was to set a state value to keep track what was processed. If the new value is the same as the previous, I can replace the text. But I can't set the state since it's in the rendering function:
Warning: setState(...): Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
So how can I keep track of the value being rendered, or is there a better way to do this?
Example of what I tried:
var stuff = [1,1,1,2,2,2,3,3,3];
var foo = React.createClass({
display: function(k) {
return <line myStuff={k}>
},
render: function() {
return (
{Object.keys(stuff).sort().map(this.display)}
)
}
});
var line = React.createClass({
getInitialState: function() {
return {
currentValue: 0
};
},
checkRepeat: function(value) {
if (this.state.currentValue == value) {
return '.'
} else {
this.setState({currentValue: value});
return value;
}
},
render: function() {
<p>{this.checkRepeat(this.props.myStuff)}</p>
}
});
Thank you!

What your code currently does:
inside <Foo> component, Object.keys(stuff).sort() turns your array [1,1,1,2,2,2,3,3,3] into a different array, containing the keys, so result = ["0", "1", "2", "3", "4", "5", "6", "7", "8"]
each of these results is then used to create a new <Line> component, passing the "0", "1" etc as a prop to the component.
The intention of the code inside the <Line> component is probably to check if the number passed is unique (same a number from <Line> component before). If so, render the number, otherwise, render "."
Main issue with this code is that you are trying inside a child (<Line>), to compare its prop with a prop of a sibling child.
The best place to put this logic of comparing numbers, is inside the parent (<Foo>).
The child (<Line>) really should have simple and isolated logic: just render whatever prop the parent sends.
To fix:
Change the render() function inside <Foo>to:
render: function() {
// copy the stuff array to a new array,
// where we replace all repeated values with a "." symbol
var stuffCopy = stuff.map((item,i) =>
{ return (i==0 || item != arr[i-1]) ? item.toString() : "." });
return (
{stuffCopy.map(this.display)}
)
}
Then you can simplify your <Line> component to:
var line = React.createClass({
// this component no longer needs state
// it can simply render the prop
render: function() {
<p>{this.props.myStuff}</p>
}
});
PS: "Previous" can have two very different meanings:
"the value of the exact same variable at a previous moment in time": looking at one component, where the state or prop had value A, and then a new prop or state value of B is sent to the one component.
"the value of the same variable, in another instance, rendered at the same time": if you render 9 components at the same time, previous value could mean, the value of the sibling component, rendered at the same moment, but appearing before this component in the list.
Your code suggests your question is about meaning nr. 2. (and my answer is focused on that meaning).

Related

Misunderstanding `each...in` rendering with reactive variable

I'm new to Meteor/Blaze but that is what my company is using.
I'm struggling to understand how Blaze decides to render what based on ReactiveDict
TL;DR
I create some children templates from a ReactiveDict array in the parent array. The data is not refreshed in the children templates when the ReactiveDict changes and I don't understand why.
I probably have misunderstood something about Blaze rendering. Could you help me out?
Parent
Parent Html template
<template name="parent">
{{#each child in getChildren}}
{{> childTemplate (childArgs child)}}
{{/each}}
</template>
Parent Javascript
Reactive variable
The template renders children templates from a getChildren helper that just retrieves a ReactiveDict.
// Child data object
const child = () => ({
id: uuid.v4(),
value: ""
});
// Create a reactive dictionary
Template.parent.onCreated(function() {
this.state = new ReactiveDict();
this.state.setDefault({ children: [child()] });
});
// Return the children from the reactive dictionary
Template.parent.helpers({
getChildren() {
return Template.instance().state.get('children');
}
});
Child template arguments (from parent template)
The parent template gives the child template some data used to set default values and callbacks.
Each is instantiated using a childArgs function that uses the child's id to set the correct data and callbacks.
When clicking a add button, it adds a child to the children array in the ReactiveDict.
When clicking a delete button, it removes the child from the children array in the ReactiveDict.
Template.parent.helpers({
// Set the children arguments: default values and callbacks
childArgs(child) {
const instance = Template.instance();
const state = instance.state;
const children = state.get('children');
return {
id: child.id,
// Default values
value: child.value,
// Just adding a child to the reactive variable using callback
onAddClick() {
const newChildren = [...children, child()];
state.set('children', newChildren);
},
// Just deleting the child in the reactive variable in the callback
onDeleteClick(childId) {
const childIndex = children.findIndex(child => child.id === childId);
children.splice(childIndex, 1);
state.set('children', children);
}
}
}
})
Child
Child html template
The template displays the data from the parent and 2 buttons, add and delete.
<template name="child">
<div>{{value}}</div>
<button class="add_row" type="button">add</button>
<button class="delete_row" type="button">delete</button>
</template>
Child javascript (events)
The two functions called here are the callbacks passed as arguments from the parent template.
// The two functions are callbacks passed as parameters to the child template
Template.child.events({
'click .add_row'(event, templateInstance) {
templateInstance.data.onAddClick();
},
'click .delete_row'(event, templateInstance) {
templateInstance.data.onDeleteClick(templateInstance.data.id);
},
Problem
My problem is that when I delete a child (using a callback to set the ReactiveDict like the onAddClick() function), my data is not rendered correctly.
Text Example:
I add rows like this.
child 1 | value 1
child 2 | value 2
child 3 | value 3
When I delete the child 2, I get this:
child 1 | value 1
child 3 | value 2
And I want this:
child 1 | value 1
child 3 | value 3
I'm initialising the child with the data from childArgs in the Template.child.onRendered() function.
Good: The getChildren() function is called when deleting the child in the ReactiveDict and I have the correct data in the variable (children in the ReactiveDict).
Good: If I have 3 children and I delete one, the parent template renders only 2 children.
Bad: Yet the child's onRendered() is never called (neither is the child's onCreated() function). Which means the data displayed for the child template is wrong.
Picture example
I am adding pictures to help understand:
Correct html
The displayed HTML is correct: I had 3 children, and I deleted the second one. In my HTML, I can see that the two children that are displayed have the correct ID in their divs. Yet the displayed data is wrong.
Stale data
I already deleted the second child in the first picture. The children displayed should be the first and the third.
In the console log, my data is correct. Red data is the first. Purple is the third.
Yet we can see that the deleted child's data is displayed (asd and asdasd). When deleting a tag, I can see the second child's ID in the log, though it should not exist anymore. The second child ID is in green.
I probably have misunderstood something. Could you help me out?
I am not sure where to start but there are many errors and I rather like to provide a running solution here with comments.
First the each function should correctly pass the id instead of the whole child or the find will result in faults:
<template name="parent">
{{#each child in getChildren}}
{{#with (childArgs child.id)}}
{{> childTemplate this}}
{{/with}}
{{/each}}
</template>
In the helper you can avoid calling too many of the Template.instance() functions by using lexical scoping:
childArgs (childId) {
const instance = Template.instance()
const children = instance.state.get('children')
const childData = children.find(child => child.id === childId)
const value = {
// Default values
data: childData,
// Just adding a child to the reactive variable using callback
onAddClick () {
const children = instance.state.get('children')
const length = children ? children.length : 1
const newChild = { id: `data ${length}` }
const newChildren = [...children, newChild]
instance.state.set('children', newChildren)
},
// Just deleting the child in the reactive variable in the callback
onDeleteClick (childId) {
const children = instance.state.get('children')
const childIndex = children.findIndex(child => child.id === childId)
children.splice(childIndex, 1)
instance.state.set('children', children)
}
}
return value
}
Then note, that in the event callback 'click .delete_row' you are using templateInstance.data.id but this is always undefined with your current structure. It should be templateInstance.data.data.id because data is always defined for all data coming in a Template instance and if you name a property data then you have to access it via data.data:
Template.childTemplate.events({
'click .add_row' (event, templateInstance) {
templateInstance.data.onAddClick()
},
'click .delete_row' (event, templateInstance) {
templateInstance.data.onDeleteClick(templateInstance.data.id)
}
})
Now it also makes sense why your data was weirdly sorted. Take a look at the onDeleteClick callback:
onDeleteClick (childId) {
// childId is always undefined in your code
const children = instance.state.get('children')
const childIndex = children.findIndex(child => child.id === childId)
// childIndex is always -1 in your code
// next time make a dead switch in such situations:
if (childIndex === -1) {
throw new Error(`Expected child by id ${childId}`)
}
children.splice(childIndex, 1)
// if index is -1 this removes the LAST element
instance.state.set('children', children)
}
So your issue was the splice behavior and passing an unchecked index into splice:
The index at which to start changing the array. If greater than the length of the array, start will be set to the length of the array. If negative, it will begin that many elements from the end of the array (with origin -1, meaning -n is the index of the nth last element and is therefore equivalent to the index of array.length - n). If array.length + start is less than 0, it will begin from index 0.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
I fixed my problem. But I still don't understand how Blaze chooses to render.
Now, the solution looks a bit like the one given by #Jankapunkt in the first part of his solution, but not exactly. The find to get the child was working completely fine. But now that I make the template rendering dependent on a reactive helper, it re-renders the template when the id changes (which it did not when it was only dependent on the child itself from the each...in loop).
In the end, I don't understand what the each...in loop does and how it uses the data to loop. See Caveats.
To give credits where it's due, I had the idea of implementing that dependency from this post.
Edits from the original code
I edit the parent template to make the child rendering dependent on its own id. That way, when the child.id changes, the template re-renders.
Html template
I added a dependency on the child.id to re-render the child template.
<template name="parent">
{{#each childId in getChildrenIds}}
{{#let child=(getChild childId)}}
{{> childTemplate (childArgs child)}}
{{/let}}
{{/each}}
</template>
Javascript
I have now two helpers. One to return the ids for the each...in loop, the other to return the child from the id and force the child template re-render.
Template.parent.helpers({
// Return the children ids from the reactive dictionary
getChildrenIds() {
const children = Template.instance().state.get('children');
const childrenIds = children.map(child => child.id);
return childrenIds;
},
// Return the child object from its id
getChild(childId) {
const children = Template.instance().state.get('children');
const child = children.find(child => child.id === childId);
return child;
}
});
Complete Code
Here is the complete solution.
Parent
Html template
<template name="parent">
{{#each childId in getChildrenIds}}
{{#let child=(getChild childId)}}
{{> childTemplate (childArgs child)}}
{{/let}}
{{/each}}
</template>
Javascript
// Child data object
const child = () => ({
id: uuid.v4(),
value: ""
});
// Create a reactive dictionary
Template.parent.onCreated(function() {
this.state = new ReactiveDict();
this.state.setDefault({ children: [child()] });
});
Template.parent.helpers({
// Return the children ids from the reactive dictionary
getChildrenIds() {
const children = Template.instance().state.get('children');
const childrenIds = children.map(child => child.id);
return childrenIds;
},
// Return the child object from its id
getChild(childId) {
const children = Template.instance().state.get('children');
const child = children.find(child => child.id === childId);
return child;
},
// Set the children arguments: default values and callbacks
childArgs(child) {
const instance = Template.instance();
const state = instance.state;
const children = state.get('children');
return {
id: child.id,
// Default values
value: child.value,
// Just adding a child to the reactive variable using callback
onAddClick() {
const newChildren = [...children, child()];
state.set('children', newChildren);
},
// Just deleting the child in the reactive variable in the callback
onDeleteClick(childId) {
const childIndex = children.findIndex(child => child.id === childId);
children.splice(childIndex, 1);
state.set('children', children);
}
}
}
});
Child
Html template
<template name="child">
<div>{{value}}</div>
<button class="add_row" type="button">add</button>
<button class="delete_row" type="button">delete</button>
</template>
Javascript
Template.child.events({
'click .add_row'(event, templateInstance) {
templateInstance.data.onAddClick();
},
'click .delete_row'(event, templateInstance) {
templateInstance.data.onDeleteClick(templateInstance.data.id);
}
});
Caveats
The solution is working. But my each..in loop is weird.
When I delete a child, I get the correct IDs when the getChildrenIds() helper is called.
But the each..in loops over the original IDs, even those who were deleted and are NOT in the getChildrenIds() return value. The template is not rendered of course because the getChild(childId) throws an error (the child is deleted). The display is then correct.
I don't understand that behaviour at all. Anybody knows what is happening here?
If anybody has the definitive answer, I would love to hear it.
Correct way of solving this issue
The correct way to fix this is to create your own _id which gets a new unique _id each time the array of objects changes. It is outlined in the Blaze docs here: http://blazejs.org/api/spacebars.html#Reactivity-Model-for-Each
This will only happen when you are dealing with #each blocks with non-cursors, like arrays or arrays of objects.
Cursor-based data together with #each blocks works fine and gets rerendered correctly, like Pages.findOne(id).
Examples if you need to deal with arrays and #each blocks
Not working
[
{
name: "Fred",
value: 1337
},
{
name: "Olga",
value: 7331
}
]
Working
[
{
name: "Fred",
value: 1337,
_id: "<random generated string>"
},
{
name: "Olga",
value: 7331,
_id: "<random generated string>"
}
]

How to initialize data in component according to the received props and don't lose reactivity

I have a problem with initialization data before component is created. My actual question depends on that problem: I'm losing reactivity in one of my data properties because I initialize it in lifecycle hook. But I don't know how to initialize an array from data(){} with a length which I receive from props. If I make it in lifecycle hook, then I'm losing reactivity, as I metioned before.
Here is some more details about my component:
In my Vue.js learning I'm trying to implement a stepper component. I decided to make it a little more dynamic and to be with a flexible size. So in my props of stepper component I receive an Object with such structure:
stepperData: {
steps: 3, //maybe later I'll add more options to stepperData, so I decided to implement it as an Object, not Array of content.
content: [
{
header: "Stepper #1",
text: "Hello World 1!"
},
{
header: "Stepper #2",
text: "Hello World 2!"
},
{
header: "Stepper #3",
text: "Hello World 3!"
}
]
}
Than in my stepper component I am using a steps field to determine a length of another array which hold data about marked or unmarked steps. Here is a code which I am using to initialize that array of marked steps:
methods: {
initializeMarkedSteps() {
this.markedSteps = [];
for (var i = 0; i < this.dataStepper.steps; i++) {
this.markedSteps[i] = false;
}
}
},
created: function() {
this.initializeMarkedSteps();
}
markedSteps is an empty array in data(){}
So, after that, I had an array of false values. In my template I have a v-bind:class
<div class="circle" v-bind:class="{markedCircle: markedSteps[s]}">
Thanks to it all of the steps are unmarked and they can became marked after user clicks "next" button.
<my-btn #click="nextStep">Next</my-btn>
my-btn is my wrapper component for simple button.
Code in nextStep():
nextStep() {
for (let i = 0; i < this.dataStepper.steps; i++) {
if (this.markedSteps[i] === false) {
this.markedSteps[i] = true;
console.log(this.markedSteps);
return;
}
}
}
BUT, when I click button, markedCircle class is not assigned as I expect despite the fact, that acual value of markedSteps[i] was changed to true after button was clicked.
I am very frustrated with this stuff with which I am so messed up. Any help will be appreciated. I have already checked docs on this theme and also I've read "Reactivity in Depth" section but I didn't saw an answer.
There are multiple problems
In your examples you don't show how you initialize your data() but assuming from the code this.markedSteps = []; in initializeMarkedSteps I think you have no markedSteps in data(). That's problem number 1. Properties in data are only reactive if they existed when the instance was created (add markedSteps: [] into data())
Due to limitations in JavaScript, Vue cannot detect changes to an array when you directly set an item with the index - use Vue.set(this.markedSteps, i, true) instead

Why is useState not triggering re-render?

I've initialized a state that is an array, and when I update it my component does not re-render. Here is a minimal proof-of-concept:
function App() {
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
console.log("rendering...");
return (
<div className="App">
{numbers.map(number => (
<p>{number}</p>
))}
<input
type="text"
value={numbers[0].toString()}
onChange={newText => {
let old = numbers;
old[0] = 1;
setNumbers(old);
}}
/>
</div>
);
}
Based on this code, it seems that the input should contain the number 0 to start, and any time it is changed, the state should change too. After entering "02" in the input, the App component does not re-render. However, if I add a setTimeout in the onChange function which executes after 5 seconds, it shows that numbers has indeed been updated.
Any thoughts on why the component doesn't update?
Here is a CodeSandbox with the proof of concept.
You're calling setNumbers and passing it the array it already has. You've changed one of its values but it's still the same array, and I suspect React doesn't see any reason to re-render because state hasn't changed; the new array is the old array.
One easy way to avoid this is by spreading the array into a new array:
setNumbers([...old])
You need to copy numbers like so let old = [...numbers];
useState doesn't update the value only if it has changed so if it was 44 and it became 7 it will update. but how can it know if an array or object have changed. it's by reference so when you do let old = numbers you are just passing a reference and not creating a new one
Others have already given the technical solution. To anyone confused as to why this happens, is because setSomething() only re renders the component if and only if the previous and current state is different. Since arrays in javascript are reference types, if you edit an item of an array in js, it still doesn't change the reference to the original array. In js's eyes, these two arrays are the same, even though the original content inside those arrays are different. That's why setSomething() fails do detect the changes made to the old array.
Note that if you use class components and update the state using setState() then the component will always update regardless of whether the state has changed or not. So, you can change your functional component to a class component as a solution. Or follow the answers provided by others.
You can change state like this
const [state, setState] = ({})
setState({...state})
or if your state is Array you can change like this
const [state, setState] = ([])
setState([...state])
I was working on an array that had objects in it and I tried few of the above.
My useState is :
const [options, setOptions] = useState([
{ sno: "1", text: "" },
{ sno: "2", text: "" },
{ sno: "3", text: "" },
{ sno: "4", text: "" },
]);
Now I want to add more options with blank field on a click of a button I will use the following way to achieve my purpose:
<button
onClick={() => {
setOptions([...options, { sno: newArray.length + 1, text: "" }]);
}}
>
This solved my problem and I was able to re render the component and added an object to the array.
introduces an array of the component that is not the one of the hook. for instance:
const [numbers, setNumbers] = useState([0, 1, 2, 3]);
var numbersModify = []; //the value you want
and at the end:
setNumbers(numbersModify)
modify this numbersModify, when the hook refreshes it will return to 0 numbersModify and the hook will keep the state. Therefore, the problem of not seeing the changes will be eliminated.
:D
//define state using useState hook
const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
//copy existing numbers in temp
let tempNumbers = [...numbers];
// modify/add no
tempNumbers.push(4);
tempNumbers[0] = 10;
// set modified numbers
setNumbers(tempNumbers);
I dont have any proud of this but it works
anotherList = something
setSomething([])
setTimeout(()=>{ setSomething(anotherList)},0)
useState is a React hook which provides functionality of having State in a functional component.
Usually it informs React to re-render the component whenever there is change in useState variables.
{
let old = numbers;
old[0] = 1;
setNumbers(old);
}
In the above code since you are referring to the same variable it stores the reference not the value hence React doesn't know about the latest changes as the reference is same as previous.
To overcome use the below hack, which will not copy the reference instead it's a deep copy(copies the values)
{
let old = JSON.parse(JSON.stringify(numbers));
old[0] = 1;
setNumbers(old);
}
Happy coding :)

Iterate over an array of objects with object having multiple properties

I'm trying to iterate over the following:
state = {
products : [
{
x : "sd",
y : "fdg"
}, {
x : "sdx",
y : "fdgx"
}
]
}
I need to iterate over the above products array and inside object to create:
<tr><td>sd</td><td>fdg</td></tr>
I tried using :
{
this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
})
}
but get multiple errors and prod being undefined.
It's possible that logic elsewhere in your component is mutating the state, which in turn may be the root cause of the error thrown during rendering.
Be sure to check that the products array is consistently defined in your components state, and that the items in that array are defined.
One solution here might be to take a more defensive approach to rendering your table row elements, by doing the following:
{
Array.isArray(this.state.products) && this.state.products
.filter(product => !!product)
.map(product => {
return <tr><td>{product.x}</td><td>{product.y}</td></tr>;
})
}
Here, the rendering logic asserts that this.state.products is the expected array type via Array.isArray(). Addtionally, the logic ensures any prop being rendered is defined by first filtering out any undefined prop items via this line:
filter(product => !!product)
Hope that helps!
The problem is that the return statement is an HTML code which is causing the problem whereas you can encode the code into a string and the DOM will treat it as HTML code
this.state.products.map(function(prod){ return "<tr><td>"+prod.x+"</td><td>"+prod.y +"</td> </tr>" }).
you need to add that in one variable return as below:
const prod = this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
});
Use the variable inside render lifecycle as below.
{prod}
Here is the working code attached in jsFiddle
Hope this helps!

Dynamically created custom form components in react

See this gist for the complete picture.
Basically I will have this form:
When you click the plus, another row should appear with a drop down for day and a time field.
I can create the code to add inputs to the form, however I'm having trouble with the individual components (selectTimeInput is a row) actually updating their values.
The onChange in the MultipleDayTimeInput is receiving the correct data, it is just the display that isn't updating. I extremely new to react so I don't know what is causing the display to not update....
I think it is because the SelectTimeInput render function isn't being called because the passed in props aren't being updated, but I'm not sure of the correct way to achieve that.
Thinking about it, does the setState need to be called in the onChange of the MultipleDayTimeInput and the input that changed needs to be removed from the this.state.inputs and readded in order to force the render to fire... this seems a little clunky to me...
When you update the display value of the inputs in state, you need to use this.setState to change the state data and cause a re-render with the new data. Using input.key = value is not the correct way.
Using State Correctly
There are three things you should know about
setState().
Do Not Modify State Directly
For example, this will not re-render a
component:
// Wrong
this.state.comment = 'Hello';
Instead, use setState():
// Correct
this.setState({comment: 'Hello'});
The only place where you
can assign this.state is the constructor.
read more from Facebook directly here
I would actually suggest a little bit of a restructure of your code though. It's not really encouraged to have components as part of your state values. I would suggest having your different inputs as data objects in your this.state.inputs, and loop through the data and build each of the displays that way in your render method. Like this:
suppose you have one input in your this.state.inputs (and suppose your inputs is an object for key access):
inputs = {
1: {
selectedTime: 0:00,
selectedValue: 2
}
}
in your render, do something like this:
render() {
let inputs = Object.keys(this.state.inputs).map((key) => {
let input = this.state.inputs[key]
return (<SelectTimeInput
key={key}
name={'option_' + key}
placeholder={this.props.placeholder}
options={this.props.options}
onChange={this.onChange.bind(this, key)}
timeValue={input.selectedTime}
selectValue={input.selectedValue}
/>)
)}
return (
<div>
<button className="button" onClick={this.onAddClick}><i className="fa fa-plus" /></button>
{ inputs }
</div>
);
}
Notice how we're binding the key on the onChange, so that we know which input to update. now, in your onChange function, you just set the correct input's value with setState:
onChange(event, key) {
this.setState({
inputs: Immutable.fromJS(this.state.inputs).setIn([`${key}`, 'selectedTime'], event.target.value).toJS()
// or
inputs: Object.assign(this.state.inputs, Object.assign(this.state.inputs[key], { timeValue: event.target.value }))
})
}
this isn't tested, but basically this Immutable statement is going to make a copy of this.state.inputs and set the selectedTime value inside of the object that matches the key, to the event.target.value. State is updated now, a re-render is triggered, and when you loop through the inputs again in the render, you'll use the new time value as the timeValue to your component.
again, with the Object.assign edit, it isn't tested, but learn more [here]. 2 Basically this statement is merging a new timeValue value in with the this.state.inputs[key] object, and then merging that new object in with the entire this.state.inputs object.
does this make sense?
I modified the onChange in the MultipleDayTimeInput:
onChange(event) {
const comparisonKey = event.target.name.substring(event.target.name.length - 1);
const input = this.getInputState(comparisonKey);
input.selected = event.target.value;
input.display = this.renderTimeInput(input);
let spliceIndex = -1;
for (let i = 0; i < this.state.inputs.length; i++) {
const matches = inputFilter(comparisonKey)(this.state.inputs[i]);
if (matches) {
spliceIndex = i;
break;
}
}
if (spliceIndex < 0) {
throw 'error updating inputs';
}
this.setState({
inputs: [...this.state.inputs].splice(spliceIndex, 1, input)
});
}
The key points are:
// re render the input
input.display = this.renderTimeInput(input);
// set the state by copying the inputs and interchanging the old input with the new input....
this.setState({
inputs: [...this.state.inputs].splice(spliceIndex, 1, input)
});
Having thought about it though, input is an object reference to the input in the this.state.inputs so actually [...this.states.inputs] would have been enough??

Categories

Resources