Semantic's Transition.Group example shown here https://react.semantic-ui.com/modules/transition/#types-group only animates the bottom most element. How would I change it so the top most element gets animated instead.
If I press the add or subtract button in the example above the animation only applies to the last element in the list, how would I make it only animate the top most element. Or essentially make the selector opposite of default so it goes from top to bottom
If you read the sample code you realize all it does is to display a slice of the array based on the current length in the state. It appears to be appending / removing from the bottom because it's taking elements starting from the index 0. If you want to show it as if it's animating at the top, you just need to take the elements starting from the last element. For example, you can do this with reverse:
#8 state = { items: users.slice(0, 3).reverse() }
#10 handleAdd = () => this.setState({ items: users.slice(0, this.state.items.length + 1).reverse() })
#12 handleRemove = () => this.setState({ items: this.state.items.slice(1) })
An easier solution using reverse would be to just reverse the array when you map through it and create the children.
render() {
const { items } = this.state;
return (<div>
{items.clone().reverse().map((item, index) => <div key={item}>
{ /* Stuff */ }
</div>)}
</div>);
}
Yes, it would create a new array on each call to render, but the key prop identifies an element and prevents unnecessary re-renders of the child elements, because though it is a new array, object references will still be the same. But I would recommend to use a more identifiable and unique value for the key prop, e.g. IDs' or slugs.
Also this way you wouldn't have to recalculate the array using reverse everywhere you use it, just at one point in your code.
Related
I'm trying to understand this piece:
https://codepen.io/GreenSock/pen/RwVgEgZ
The hard thing for me is to understand the select property. AFAIK, this is a property of a <select> HTML element, that it is no present in this HTML. And a particular use of forEach.
This is the JS as I understand:
// toArray GSAP tool
// https://greensock.com/docs/v3/GSAP/UtilityMethods/toArray()
// Store an array with .accordion-group elements
let groups = gsap.utils.toArray(".accordion-group");
// Store an array with .accordion-menu elements
let menus = gsap.utils.toArray(".accordion-menu");
// Apply createAnimation(element) for each array element
// This creates animations for each .accordion-group
// and store it in a variable
let animations = groups.map(createAnimation);
// Add click event listener to each .accordion-menu
// that fires playAnimation(selected) on click
menus.forEach(menu => {
menu.addEventListener("click", () => playAnimation(menu));
});
//
function playAnimation(selected) {
// I don't undestand this particular use of forEach
// what means animate => animate(selected)?
// what means selected? I search this property on MDN web docs with no luck
animations.forEach(animate => animate(selected))
}
// CreateAnimation function
function createAnimation(element) {
// Create colections of .accordion-menu and .accordion-content
let menu = element.querySelector(".accordion-menu");
let box = element.querySelector(".accordion-content");
// GSAP initial set height of .accordion-content to auto
gsap.set(box, { height: "auto"})
// GSAP tween reversed. I have no problem with this
let tween = gsap.from(box, {
height: 0,
duration: 0.5,
ease: "power1.inOut"
}).reverse();
// CreateAnimation() returns the tween reversed if it is not selected
return function(selected) {
// Ternary operator.
// Store true in the reverse variable if menu is not selected
// Get !tween.reversed() (This means true if tween is not reversed or false if it is reversed) and store it in reversed variable.
let reversed = selected !== menu ? true : !tween.reversed();
// return tween reversed or not reversed regarding reversed variable
tween.reversed(reversed);
}
}
In short, what I want to know is: what does this mean: animate => animate(selected)? What means selected? I searched this property on MDN web docs with no luck.
While chliang is not wrong, I feel like explaining the process of what's going on in general is valuable as I think your understanding is not quite correct.
Here's the steps of what happens when the JS is ran:
The DOM elements are selected.
The groups are looped through and an animation for each group that animates the height of that specific group is created. This animation will be re-used each time that the group is animated (i.e. a new animation will not be created, only the state of this animation will be changed — this is a good technique of animating efficiently).
With that being said, what is actually returned is not a reference to the animation itself but rather a function that controls the reversed state of the animation. It'd probably help to look at the .reversed() documentation.
The menu items are looped through, adding a click event listener to each. The event listener for each menu item passes in that menu item to the function that was returned in the last step. Inside of the function (returned from the last step), if the menu item is the same as the one that is clicked, the reversed state for that animation is set to the opposite of what it currently is, usually being set to true, meaning the animation will play forwards. However if it's the same one that's clicked and it's reversed state is already false this means that it's already opened or opening, so its reversed state will be changed false.
For all other animations, the reversed state will be set to true, meaning it will play backwards if its progress is not 0. It will do nothing if its progress is 0.
To be clear, the HTML <select> element is not being used and is unrelated to the demo. It achieves similar behavior but cannot be customized to behave like this accordion.
With all of that being said, I probably would have written this code in a different way to be more readable, like this.
Let me know if you have any more questions.
First of All "animations" is an Array which has 5 functions .
animations.forEach
will pass every function to the brackets next to it,suppose the first function is fun1 .
selected is the param of The Function "playAnimation" , in fact selected is one menu of the menus ,then passed to the function eg. fun1 , fun1(selected) triggers.
replace fun1 to animate .
I have a list has many objects in it, like this:
[{id: 3, kategori: 'Kultur', name: 'Topkapı Sarayı',},{id: 5, kategori: 'Diger',name: 'Topkapı Sarayı', description: "xxxx"},]
and I want to return objects of this list one by one on a card by (prev, next) buttons. so I created a counter and defined the increment (for next button) and reduce(for prev button) functions. Then I used this counter as index of the list. Finally I mapped this list like that:
this.viwedList = [ myList()[this.state.counter] ].map((tur) => (
<CreatContent
turName={tur.name}
img={tur.img}
country={tur.country}
increment={this.increment}
reduce={this.reduce}
/>
));
this.setState(() => {
return {
mainContent: this.viwedList
};
});
When I click on the prev and next buttons, the counter value increases and decreases. The main problem is that the displayed object does not change instantly, when I click on another category (list) and then return to the same card, then it changes.
[first img of issua][1] [second img of issua][2] [last img of
issua][3]
[1]: https://i.stack.imgur.com/mjzRf.jpg [2]:
https://i.stack.imgur.com/7tCVA.jpg [3]:
https://i.stack.imgur.com/qqS2E.jpg
When you render objects from an array, they need to have a unique key property. This lets React keep track of them, so it can know what things need to be updated on a new render.
Without a key, React may fail to update the components, as it fails to recognize that they have changed, which is the behavior you're seeing.
You can read more in the docs.
I have a function that find an object from a JSON that has an id === this.match.mainParticipant.stats.perkSubStyle. This object contains a property called slots that is an array and has 4 elements. Each slot has 3 elements which represent runes from a game. If you iterate over the slots and their elements you get this:
I get the object using this function:
secondaryPerks(){
let perksTree = this.$store.state.summonerRunes.find(value => value.id === this.match.mainParticipant.stats.perkSubStyle);
console.log(perksTree.slots.unshift())
return perksTree
}
and I iterate and display the icons using this:
<div v-for='runes in this.secondaryPerks().slots'>
<div v-for='rune in runes.runes'>
<img :src="'https://ddragon.leagueoflegends.com/cdn/img/' + rune.icon" alt="">
</div>
</div>
Now the problem is that because that perks tree is secondary one, the perks in slot[0] can never be picked because if they were picked, they'd have to be part of the primaryPerks tree. This means there's no point displaying that none of them were selected. For that reason I am trying to remove the first slot[0] element from the array, however, when I try to unshift() it, I get an error:
"You may have an infinite update loop in a component render function"
And I have no clue why. Any advices?
Firstly, I think you mean shift rather than unshift. unshift will try to add items to the array rather than removing them. It doesn't actually matter from the perspective of the infinite loop, either method will have the same effect.
You're creating a dependency on the array and then modifying it. Modifying it will trigger a re-render.
Each time the component re-renders it will shift another item onto/out of the array. Even if the call to shift/unshift doesn't actually change anything it will still count as modifying the array.
Try:
computed: {
secondaryPerkSlots () {
const perksTree = this.$store.state.summonerRunes.find(
value => value.id === this.match.mainParticipant.stats.perkSubStyle
);
return perksTree.slots.slice(1)
}
}
with:
<div v-for='runes in secondaryPerkSlots'>
That will create a new array containing the same elements as the original array, omitting the first element.
Alternatively you could put the slice(1) directly in the template:
<div v-for='runes in secondaryPerks().slots.slice(1)'>
Either way I suggest changing the method to a computed property instead. You should also drop the this in your template.
I had the same problem a few months ago.
I think the main issue is that you perform logic such as arr.unshift()(which will cause the template to re-render in this case) in your computed property.
So, imagine this:
const arr1 = [/* ... */];
// This is different
const computedArr = () => {
return arr.filter(() => { /* ... */ });
};
// Than this
const computedArr = () => {
const newArr = arr.filter(() => { /* ... */ });
// Vue cannot allow this without a re-render!
newArr.unshift();
return newArr;
};
The latter will cause the template to re-render;
EDIT
Check the first comment!
I have created an array as follows :
const title = ['hello', 'secondTitle', 'thirdTitle']
And I have a component where I need to pass title as property,
I also have a service that returns an object with members : HelloContent, secondtitleContent, thirdTitleContent
<CustomComponent listTitle = {title} toggleExpanded= {toggleExpanded} pass corresponding data?>
I also have a state variable :
constructor(props) {
state: {
expanded: false
}
}
toggleExpanded = () => {
this.setState({expanded: !this.state.expanded})
}
So what I want to do is :
Iterate through the array,
Pass the title to the CustomComponent and I'm also passing toggleExpanded ( which expands and collapses the view).
But how do I pass for every item in the array to expand its current view, because this way, if I expand one, all components expand.
Do I change expanded to be an array [] ?
Any suggestions ?
I am confused as to what approach to use, because I did not want to repeat the customComponent and call it 5-6 times ( as many items on the list and data),
but how do I pass and iterate the right ones ?
I wouldn't use boolean in this case. Simply set expanded to the title you need to expand and update your component to compare if expanded is equals to own title. If so, expand it.
In ExtJS panel I need to set value of all items (e.g. textfield, pathfield) to blank. I don't want to set value of each individual item to blank but of whole panel in one go.
I am able to get list of items
function getAllChildren (panel) {
/*Get children of passed panel or an empty array if it doesn't have thems.*/
var children = panel.items ? panel.items.items : [];
/*For each child get their children and concatenate to result.*/
CQ.Ext.each(children, function (child) {
children = children.concat(getAllChildren(child));
});
return children;
}
but how to set to blank for whole panel? Please suggest what need to be done in this case.
Actually, it's not possible to do it with one liner - all at the same time. What your method returns is purely an array of objects. In fact if such syntax existed, it would iterate over all fields anyway.
Though clearing all fields, having the method you've proposed is very trivial to do. Just iterate over them all and call reset method. Mind some (especially custom) widgets might not handle it.
var fields = getAllChildren(panel);
CQ.Ext.each(fields, function(field) {
if (child.reset) {
child.reset();
}
});
You've got similar loop in your getAllChildren code - you might reset field at the same place.
The method is defined in Field type which is usually a supertype of each dialog widget. You can read more here.