Using react-spring useSprings with dynamic items array - javascript

I am attempting to use react-spring's useSprings to enable users to reorder the items in a formik FieldArray. The useSprings Draggable List demo (found here) uses useRef to manage the order of items. FieldArray comes with a number of array helper functions for inserting, removing, and moving items.
The issues that I'm having are:
1) Re-ordering existing items using formik's move array helper method successfully changes the array order, but requires an additional click to render the correct order
2) Adding or removing array items using array helper methods produces unexpected results. Mutating the length of the ref doesn't change the length of order.current inside of useGesture
I've also tried using useState instead of useRef and updating the state with useEffect when props change.
Here is a code sandbox I made: https://codesandbox.io/s/usesprings-with-fieldarray-56bex
In the bind function, commenting out order.current = newOrder; and uncommenting // arrayHelpers.move(currIndex, currRow); shows issue #1 that I mentioned above.
I would like to be able to use formik's move, insert, and remove helper functions with react-spring to seamlessly re-order, add, and delete items within a FieldArray.

maybe you can try setting the new order.current after adding the new element
onClick={() =>{
arrayHelpers.insert(items.length, {
name: `Item ${items.length + 1}`
})
order.current = [...order.current, order.current.length];
}
}
this will add the new item at the end of the list.

I encountered at least your #1 issue.
Note that the the setSprings function doesn't re-render anything on its own, and the useSprings is missing a dependencies array to auto-update.
react-springs#9.0.0.beta-23 has a dependencies array, and together with the useSpringsFixed wrapper in the sandbox that is linked here it should force-rerender on changed props.
Hope that helps your issue too.

Related

Stay subscribed to an array of Subjects

I have a main file that creates and destroys instances of a class Child. The Child instances are kept in an array (childArr) within main and I want a way to listen for events from all of those Child instances.
Each Child has a Subject exposed and I want to keep a subscription to all those Subjects that remains up to date as Child instances are added to or removed from the Array.
This stackblitz project is the simplest version I can come up with. As it is it works while I manually add each child subject to the merge() method (lines 22-24). The commented out lines at the bottom (lines 28-30) are what I had hoped to do but that don't seem to work.
I would love someone to explain the difference between the merge on lines 22-24 and the one on line 28-30, why the first works and the second doesn't and hopefully how I can make the second one work.
Any help appreciated.
The behavior of merge operator, when passed an ArrayLike is much like FROM operator where each item in the array is wrapped with a new Observable.
Simply put:
merge([1,2,3]) equals to from([1,2,3)]
Passing merge an array of Observables would results in each item getting wrapped with a new Observable as demonstrated below:
merge([of(1), of(2)]).subscribe(x => {
console.log(isObservable(x)) // true
});
To avoid that consider using the SPREAD operator as in the following example:
merge(...childArr.map(x => x.feed$)).subscribe(e => {
console.log(e);
});

React - Re-arrange/swap elements in an array map

I'm creating a simple list that I'd like to be able to "sort / rearrange" via up/down arrows, like so : https://codesandbox.io/s/stupefied-kilby-thcd6?file=/src/App.js
I am currently using array.splice to return my "from" item, and then once again using splice to insert the "from" item at its desired location
I can currently see this occurring "properly" in the console.logs, however I am unable to re-render my map to reflect the index positions, I have provided my map with a proper key (the ID's), and so I believe I've covered my base there.
When calling setState with my "sorted" array, I can see no visual change, and I'm looking for suggestions, thanks!
Just use the spread operator when setting options as shown below:
setOptions([...newOptions]);

Select all chekboxes by $emit doesn't re-renders view - Vuejs

Case - Need to select/unselect all checkboxes on change of header checkbox.
Problem - I can emit and receive events. Can see my modified data array. But some reactivity isn't working in Vue I guess.
I have laid out very minimal cut out example for same on CodeSandbox - https://88nz9z64j0.codesandbox.io/
Code -
https://codesandbox.io/s/88nz9z64j0
Note - This has to do something with Array mutation. As I can select individual checkboxes and can see my header checkbox marked check after all are selected. However, reverse is just not happening via same mechanism. How Strange!
There has to be a learning from Array(100).fill(length) as reactive data else I'm doing something silly.
The problem is that Array.fill was modifying the array in place. Vue can't detect changes to an array when you directly modify its elements. You need to create a new array first using split, modify the newly created array and then re-assign it.
this.checkSelections = this.checkSelections.splice(0).fill(allSelected);
CodeSandbox
you should check this article about reactivity in vue and it pitfalls https://medium.com/js-dojo/reactivity-in-vue-js-and-its-pitfalls-de07a29c9407, and certain the section on arrays.
For arrays you better make use of the Vue.set
see fork : https://codesandbox.io/s/m7xqk9qn0j
for(var i = 0; i <= this.checkSelections.length; i++)
{
Vue.set(this.checkSelections, i , allSelected);
}
detailed information can also be found in on the official vue site:
https://v2.vuejs.org/v2/guide/list.html#Caveats
(in this case the length of the array does not change, so caveat1)
On your mounted function on table/index.vue you can use this.$forceUpdate() after changing the array, since vue can't see the changes.
mounted() {
this.$root.$on("selectAll", ({ allSelected }) => {
console.log("selectAll index.vue", allSelected);
this.checkSelections = this.checkSelections.fill(allSelected);
console.log("selectAll", this.checkSelections);
this.$forceUpdate();
});
}
You can see more details on the documentation
Working example

React list with no keys

I have an array of number that I wish to render in a tabular form. The array is returned from an API call, not generated by my app.
The data may change but is unlikely to do so, and in any case there are only twenty odd values, so re-rendering the whole table is not really a problem.
A simple data.map(value => <td>{value}</td> should do it.
But I keep getting an Each child in an array or iterator should have a unique "key" prop. warning. Is there any way that I can tell React that there is no key and that I wish it to re-render the whole table if anything changes.
Alternatively, is there any way that I can generate a unique key for each entry? The data items are not guaranteed to be unique.
I should add that I understand what keys are for and why they are useful, but in this instance I do not have any and the easiest thing would be not to use them, since there is unlikely to be a re-render.
You can use the index as the key. I think its worth reiterating that using the index as the key only works fine in the very specific scenario that the OP is facing.
This is particularly annoying when requirements change and all of sudden the list is being modified. This shows up as items not being updated during the render because the item updated has the same key (the index), its value is different, but react only cares about the key.
In cases where your data has no unique key. You should use some function that generates a unique id for each item. A simple version of that function just increments a global counter:
// Declared globally (as in attached to window object or equivalent)
var myuniqueidcounter = 0;
function uniqueId() {
myuniqueidcounter += 1
return myuniqueidcounter;
}
// Do this in the props change or whereever your data gets passed in
let keyedData = data.map(value => Object.assign(value, { Id: uniqueId() });
// In render
data.map(value => <td key={value.Id}>{value}</td>
That way, on multiple render calls, the ids returned are always unique. We assign the key when we get the data to avoid having to re-render the entire list on each call to render().
However, this case is actually pretty rare as you can usually find some combination of the backing data that will produce a unique key for each entry.
If you do go index-as-key
This article lists 3 conditions that should be met when choosing index-as-key approach that I think is a good check list:
The list and items are static–they are not computed and do not change;
The items in the list have no ids;
The list is never reordered or filtered.
data.map((value,index) =>{
<td key={index}>{value}</td>}
)
or
data.map((value,index) =>{
let i = Math.floor(Math.random() * 1000+1)
<td key={i}>{value}</td>}
)
You can use index as your key as it is unique each time
Based on the question asked, it might be worth saying that there is an another solution to this that doesn't use keys:
e.g. The following will complain about not having unique keys:
React.createElement('div', {}, [<span>1</span>, <span>2</span>]);
However, the following renders all children with no problems (This is what JSX transformed to JS looks like for nodes with multiple children):
React.createElement('div', {}, <span>1</span>, <span>2</span>);
So if you have e.g. a smallish list of generated react element fragments and unique keys don't offer and advantage in your situation, you can do:
React.createElement.apply(null, ['div', {}, ...elementList])
Notes:
elementList is passed as arguments to React.createElement which might be an issue if the list is huge.
It will re-render all the children with each render.
Using unique keys is generally the recommended approach, and is more performant for re-rendering.
However there are occasions where you just want to render in a single shot and don't care about re-rendering, or the data is not structured in a way that you can make good use of unique keys. You can use this as a work-around if you really need to.

What is the significance of keys in ReactJS?

I want to understand what happens if I don't use keys in dynamically added components. I removed keys and it renders without any issue and just gave warning messages regarding key usage. Would someone please give some example of what the consequences are if we don't use keys?
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
Example:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
TL;DR Use unique and constant keys when rendering dynamic children, or expect strange things to happen.
One of the tricky aspects I've found during the few weeks I've been using React.js is to understand the key property you're expected to pass to a component when it's part of an array of children. It's not that you have to specify this property, things will work most of the time apart from getting this warning on the console:
Each child in an array should have a unique "key" prop. Check the render method of undefined.
By reading the linked documentation it can be easy to not see the implications of this affirmation:
When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).
At first it looked to me it was all about performance but, as Paul O’Shannessy pointed, it's actually about identity.
The key here is to understand not everything in the DOM has a representation in React "Virtual DOM" and, because direct manipulations of the DOM (like a user changing an value or a jQuery plugin listening an element) are unnoticed by React, not using unique and constant keys will end up with React recreating the DOM node of a component when the key is not constant (and losing any untracked state in the node) or reusing a DOM node to render another component when the key is not unique (and tying its state to this other component).
Here you have a live demo showing how awful the results are:
http://jsfiddle.net/frosas/S4Dju/
Just add an item, change it, add more items and see what happens.
Also see
Source
Another useful usage of React keys other than creating dynamic elements is reseting elements when their keys change, for example in a project I had an <input/> element of type file and I wanted the element to be initialized to its initial value (no file chosen) each time the component renders, so I did the following:
Parent constructor:
this.state = {
fileInputKey: Date.now()
// other properties
};
The state object also had other properties, I just added this one for the sake of this example
Each time I wanted the input element in the child component be reset I did:
this.setState({fileInputKey: Date.now()});
Parent render:
<Child fileInputKey={this.state.fileInputKey}/>
Child render:
<input key={this.props.fileInputKey} type="file" onChange={this.onSelectFile}/>
Also see this example from React blog:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key

Categories

Resources