I have a list of items (ResultItem), which have a component inside (ResultTag) which when clicked, shows a tooltip above it (a HTML class is added to it and removed when clicked again, to hide it).
However, when I click on ResultTag, and then click on ResultTag in one of the ResultItem's below it, both show; how would I go about hiding all of the ResultTag's apart from the one I just clicked on, so that only one can show at a time.
Currently, in the ResultItem, I have an onClick function which sets the state showTooltip in the ResultTag to false/hidden (using props) whenever the user clicks anywhere within ResultItem and the ResultTag is visible. However, I need this to work across every ResultItem, which means working cross-component.
Here is some simplified code:
/* ResultTag */
showTooltip() {
this.setState({ showTooltip: true })
}
render() {
return (
<div onClick={this.showTooltip}>
{this.renderTooltip()} { /* function which contains the JSX/HTML to show the toolip */ }
<span className="tag--label">Tags</span>
</div>
)
}
Hiding is done in the ResultItem, by setting the state and then receiving that as props in the ResultTag.
To summarise:
I have many ResultItem components in a list view
Each ResultItem has a ResultTag in, which when clicked, shows a tooltip above the tag/label
When a ResultTag is visible, and another one in a different ResultItem is clicked, hide all the other ResultTags
You could move the state from within each individual ResultItem into the parent, that way it is centralized in one place and you could enforce logic such that only a certain ResultItem will show its tooltip. You would need to manage the state from within the parent and then pass down a function to each ResultItem (and probably down again into its ResultTag) to handle the click.
I wrote a sample app which shows similar behaviour (although slightly different), I wrote it to demonstrate how to add a border to each item in a list. You can see how I stored the state in the parent and how I passed down the means to read and update it to the children via props. You would of course have to change the logic to enforce only a single item being active, currently it supports any item in the list being 'active'. I wrote it for an answer located here: https://stackoverflow.com/a/38646533/1515758
Related
I have a react app similar to what is shown in this codesandbox link
https://codesandbox.io/s/eatery-forked-t44bx?fontsize=14&hidenavigation=1&theme=dark
Clicking the Menu at the bottom center of the page renders the MenuFilter component.
The way I have it now, checking the checkbox immediately updates the checked items. This is good from a user interface point of you.
User gets instant feedback on what he is clicking on (what is getting un-clicked if something is getting un-clicked).
The problem with though is that the OK/Cancel button loose there use. In other words, after making changes to the selection, if the user decides that we does not want any of these new selection & rather revert back to old selection (the one before the Menu button was clicked), he cannot do that because all the states have been updated (updating happens as the check boxes are being clicked).
How to update the state ONLY if OK (currently only CANCLE button is working, so you can consider that) button is clicked. However, the check-marks should none the less change as the user is clicking them.
Is there a way to do this other than creating a 'temporary' state (I don't want to do this) to update the visual changes & only when a button is clicked, the changes are done one the data.
You can change the checkedItems to a local state of the MenuFilter component, and pass it as an argument on onClick. That way, when you open the modal, the component mounts again and the state should return to its default.
Something like:
export const MenuFilter = ({ onClick, onCheck }) => {
const [checkedItems, setCheckedItems] = useState(["allItems"])
return (
<button onClick={() => onClick(checkedItems)}>Cancel</button>
)
}
Edit: You'll still need some sort of state to handle the data on the parent component, but you won't have to manually handle resetting the data.
Been hacking at this for days, hopefully there are some Vuetify wizards around.
Here's the situation: I have rendered a set of v-expansion-panels and if one of them is expanded, it also shows an arrow button (display: none is toggled by clicking on v-expansion-header). Upon that button click, my aim is to show a dialog.
Problem: Once dialog is prompted with the button click, the button display toggle is reversed. It disappears as soon as you click on the button to prompt a dialog, and appears again once the v-expansion-panel is collapsed.
How it should be: The arrow button should always be visible as long as the v-expansion-panel is expanded, regardless of whether it is clicked to see the dialog or not.
Here's a codepen replicating and illustrating the problem.
Any thoughts would be much appreciated!
It has to do with using style directly on the element.
Use v-show instead of toggling the styles by hand:
<v-btn v-on="on" class="ml-1" width="36px" v-show="expanded[i]">
Update your data to hold an array for the pannels
data () {
return {
dialog: false,
expanded: [false, false, false]
}
}
And update your toggleMoveUp method to update expanded instead of using HTML ids.
toggleMoveup(i) {
this.$set(this.expanded, i, !this.expanded[i])
this.show=true;
}
Notes:
You need to use Vue.set when updating an array
You should not rely on HTML ids, if you use your components in more than one place at a time you'll run into multiple ids.
Why didn't your approach work? I'm guessing that vuetify is updating an element's style property, but doesn't take care of retaining already existing values so your display:none gets erased.
Posting a solution a colleague helped with. This also works with any array size (which is more of a real life scenario in dynamic webapps). It implements a created() lifecycle hook that adds an expanded: false property to each element in the array, which we can use to keep track of the expand state and toggle the button visibility. Here's the codepen.
However, in general, it is recommended in this scenario to actually make an independent component <v-expansion-panels /> and in the parent component actually loop the components. That would solve the state problems on its own already, since each component maintains their own state in their scope.
I am new with react-spring. I am having trouble animating a component whenever it unmounts. I have a simple card with an onClick handler that's responsible for conditionally displaying my Overlay component. The animation works fine when mounting (from & enter works), but when closing the overlay, the component just disappears without animation (leave does not work). I suspect it's because of the conditional rendering of the component but I've been struggling for hours trying to find a solution for this one. Any help would be appreciated!
My current code: https://codesandbox.io/s/dry-leftpad-h3vmv
What I'm trying to achieve: https://codesandbox.io/s/048079xzw
P.S. The latter is using mauerwerk's lib. I don't want to use that.
What you were missing is this:
return expand.map(({ item, props, key }) => (
item && <animated.div
// ...etc
When you're controlling the mounting of a single component with useTransition, you need to conditionally render it based on the item being passed. In your case, when it's false it won't render (which will unmount if already mounted) and when it's true it will render (mount if unmounted).
Here's a working sandbox forked from yours: https://codesandbox.io/s/infallible-agnesi-cty5g.
A little more info
The first argument to useTransition is the list you want to transition. That watches for changes and sends back an array mapped with each item, a key and a style object (props) based on whether the item is truthy (entering) or falsy (leaving). So for a transition that mounts/unmounts a single element, conditionally rendering based on the truthiness of the item is key.
Check out the examples again here and you'll see the differences between transitioning a list, a toggle between two elements, and a single item.
For a list, no need to check for the existence of the item because the array changes.
For toggling between two elements, you use the truthiness of item to determine which element to render.
For a single element, item determines whether to render at all. This means it won't mount initially when you default to false, and will make sure you don't render 2 items whenever your isActive value changes.
I have a parent component which is a set of child filter menu components. Each child component has a group of radio buttons, and the buttons all determine whether they are checked via their state.
In the parent component, when there is at least one filter for which the user has made a selection, a list of buttons appears of the selected filter options.
When one of these buttons is clicked, it should call a function within that child component to reset its radio groups.
I have seen here and here how to call a function within a single child ref when that ref is known, but how would I accomplish this when the refs are generated in a loop, and I want to only to call the ref of the specific child component that matches the button that was clicked?
Here is the relevant [pseudo-code]:
//FILTERS.JS
{filters.map((filter, index) => {
return (
<FilterContainer
key={`filter--${filter.id}`}
//NEED UNIQUE REF HERE THAT I CAN CALL
/>
)
})}
handleClearSelectedFilter = ()=>{
this.[matchingFilterContainerRef].current.resetRadioGroup();
}
where matchingFilterContainerRef would be the dynamic ref I match to the button that was clicked for the corresponding filterContainer child component.
With hopes I understand your issue correctly, I would like to suggest a different approach.
In the parent component you have a state which hold the filters. each of these filters has its own set of values. The parent then maps over the filters and passes each FilterContainer the props it needs, like the values for it, and also a callback function to when there was a change.
According to the values, the FilterContainer knows how to render its radio buttons. Data is flowing from the parent to the child in a single direction.
Once a radio button is clicked, you will call the callback given by the parent with the filter field and current value, e.g. a gender radio button was clicked, you will call this.props.onValueChange('gender', 'female').
The parent will then receive the callback and set its filters state accordingly. The state changes, the filters get rendered again with the new data.
Hope this helps :)
You can do like this
{filters.map((filter, index) => {
return (
<FilterContainer
key={`filter--${filter.id}`}
ref={`ref_${filter.id}`}
//NEED UNIQUE REF HERE THAT I CAN CALL
/>
)})}
And when you want to access it:
this.refs[`ref_${filterId}`]
See more here reactjs-dom-dynamic-refs
How can I rerender component with state from getInitialState()?
For example: https://jsbin.com/pajapoyipo/edit?html,output
In example code I click on the button, component Button change state and button is Yellow - its work, but when I click on another button I would like to clicked the button was yellow (its work), and the old button changed state and color to red (it doesn't work).
Avoid stuff like this:
childState.setState({klasa: "yellow"});
You should not be setting child state from the parent. State is internal to a component, it should only be changed by itself. Instead, the parent can re-render the child with different props.
If you want only one button to be active, then you have to have some piece of state that will only permit a single active button. Since children can't know about their fellow siblings, this piece of state must reside in the parent. I've called it activeBtn. If activeBtn is 1, then the first button appears yellow. This piece of state in the parent corresponds to the boolean property active in child. I've also defined a click handler in the parent to change the state. This click handler is passed down to each child as a prop.
If that doesn't make sense, the modified code should serve as a better explanation:
https://jsbin.com/filasisemu/1/edit?html,output