React scroll to element (ref current is null) problem - reactjs

I have a problem I'm not able to solve. The app got a component where a do looping array and making multiple elements off it. Then I want to make buttons in another component that will scroll to a specific element. (something similar to liveuamap.com when you click on a circle).
I tried the below solution, but got "Uncaught TypeError: props.refs is undefined". I could not find any solution to fix it.
The second question: is there a better or different solution to make scrolling work?
In app component I creating refs and function for scrolling:
const refs = DUMMY_DATA.reduce((acc, value) => {
acc[value.id] = React.createRef();
return acc;
}, {});
const handleClick = (id) => {
console.log(refs);
refs[id].current.scrollIntoView({
behavior: "smooth",
block: "start",
});
};
The refs I send to the article component as a prop where I render elements with generated refs from the app component.
{props.data.map((article) => (
<ContentArticlesCard
key={article.id}
ref={props.refs[article.id]}
data={article}
onActiveArticle={props.onActiveArticle}
activeArticle={props.activeArticle}
/>
))}
The function is sent to another component as a prop where I create buttons from the same data with added function to scroll to a specific item in the article component.
{props.data.map((marker) => (
<Marker
position={[marker.location.lat, marker.location.lng]}
icon={
props.activeArticle === marker.id ? iconCircleActive : iconCircle
}
key={marker.id}
eventHandlers={{
click: () => {
props.onActiveArticle(marker.id);
// props.handleClick(marker.id);
},
}}
></Marker>
))}
Thanks for the answers.

Ok so i found the solution in library react-scroll with easy scroll implementation.

Related

React Typescript: different clickhandler on specific parts of same div

I'm new to React and Typescript and Coding in general so I'm not sure if that what I'm trying to do is even possible. I have a donut chart with clickable segments. it's from a minimal pie chart: https://github.com/toomuchdesign/react-minimal-pie-chart.
So as you see the chart is round but the container is square. When I click on the segment I can check other statistics. but i want to reset it when I click on some empty place. Right now with the clickawaylistener from material UI or my own clickhandler i have to move the mouse outside of the square and can't just click next to the segments to reset since the clickaway is outside of the element. Any suggestions on how to solve this?
this is my chart with the onClick handler:
<PieChart
className={classes.chart}
onClick={handleSegment}
segmentsStyle={handleSegmentsCSS}
lineWidth={20}
label={handleChartLabels}
labelStyle={{
fontSize: "3px",
fontFamily: "sans-serif",
textTransform: "capitalize",
}}
labelPosition={115}
paddingAngle={5}
radius={30}
data={data}
animate
animationDuration={500}
animationEasing="ease-out"
/>;
And this my Clickhandler:
const handleSegment = (event: any, index: any) => {
const values = Object.values(SegementDataType).map((value, index) => ({
index,
value,
}));
setSegmentValue(values[index].value);
setStyles(segmentStyle);
setSelectedSegmentIndex(
index === selectedSegment ? undefined : index
);
};
And my Clickawaylistener is just a function to set initial values
Ok, honestly, I don't have a lot to work with (the link you posted on the comment is a not-working example).
Though, I managed to understand something from here: https://toomuchdesign.github.io/react-minimal-pie-chart/index.html?path=/story/pie-chart--full-option
Anyways, try to:
Wraps the <PieChart> component into an element (<div>, for instance).
Adds an event listener on that wrapper element.
In the listener, check if a path has been clicked or not. If not, you can deselect the item.
Something like this:
const divRef = useRef();
const handler = (e) => {
const divDOM = divRef.current;
const closestPath = e.current.closest('path');
if (closestPath != null && divDOM.contains(closestPath)) {
// Here, a segment has been clicked.
}
else {
// Here, a segment has NOT been clicked.
}
}
return (
<div onClick={handler} ref={divRef}>
<PieChart ... />
</div>
);
I also check that divDOM contains closestPath so that we are sure we are talking about a path belonging to the <PieChart>.
Though, this solution does not fix the problem that, INSIDE the <PieChart> component, the segment remains clicked. I don't think this can be fixed because of the implementation of the chart (it's a stateful component, unfortunately).
What you can try is to mimic a click on the selected path, but I don't think it will work

What is the best approach for adding class and styles without re-render - reactjs

I am building a slider and need assistance with the "best" way of implementing the feature. I have a Slider Component which receives children of SliderItems. I clone the children in Slider Component and add props. When the user clicks next or previous button I use a state isAnimating to determine if the slider is moving and add/remove styles based on isAnimating state but it was causing a re-render of slider items. I need to add animating class without causing a re-render to the enter slide items. Is there a way to implement such feature?
SliderContainer.js
<Slider totalItems={totalItems} itemsInRow={itemsInRow} enableLooping={true} handleSliderMove={handleSliderMove}>
{items.map((item) => {
return <SliderItem key={\`${item.id}\`} data={item} />;
})}
</Slider>
Slider.js
const onSliderControlClick = (direction) => {
const [newIndex, slideOffset] = sliderMove(direction, lowestVisibleIndex, itemsInRow, totalItems);
setisAnimating(true); //Causes rerender
movePercent.current = slideOffset();
setTimeout(() => {
ReactDOM.unstable_batchedUpdates(() => {
setisAnimating(false);
setHasMovedOnce(true);
setLowestVisibleIndex(newIndex());
});
}, 750);
};
<div ref={sliderContent} className={`slider-content`} style={getReactAnimationStyle(baseOffset)}>
React.Children.map(children, (child, i) =>
React.cloneElement(child, {
key: child.props.video.id,
viewportIndex: properties.viewportIndex,
viewportPosition: properties.viewportPosition,
})
);
})
</div>
use node-sass library. And make styles.modules.scss file for styling, write down css classes. And conditionally you can apply classes on any element like this.
for example -
className={props.count > 2 ? classes.abc : classes.xyz}

React hooks - Dispatching a reset state from parent component

This is my application with the scenario reproduced, here the demo in codesandbox
I have two components, Leagues ( parent ) and Details ( Child ).
I have a implemented reset button example in the Details Component button which does
const cleanArray = () => {
setDataHomeTeam([]);
};
<button onClick={cleanValue} type="button">Reset</button>
You can see in the demo that is emptying out an array of a team stat
My question is, can i implement the same button but out from Details component and from the parent component Leagues for example? Whats the way to achieve it?
I thought to go this way but i can not get it done.
So in my Details.js
let Details = forwardRef(({ ....
const cleanArray = () => {
setDataHomeTeam([]);
};
useImperativeHandle(ref, () => {
return {
cleanValue: cleanValue
}
});
in App.js
<Leagues/>
<button onClick={cleanValue} type="button">Reset</button>
<Details ref={ref} />
I get this error : 'cleanValue' is not defined no-undef
is it something that i can not do with react? How can i achieve it?
I think your approach sounds correct except for lacking the way of calling the api cleanValue you exposed. Basically you have to call it via a ref you pass to it as following:
function App() {
const ref = useRef();
return (
<>
<Leagues />
{/* call it via ref */}
<button onClick={() => ref.current.cleanValue()} type="button">Reset</button>
<Details ref={ref} />
</>
)
}
Codesandbox link: https://codesandbox.io/s/nifty-raman-c0zff?file=/src/components/Details.js
I don't completely understand what you are trying to do, but here is a solution I think is going to work for your problem
let's say you wanna filter that array with the selected team which is liverpool, first if you have control over the incoming data I recommend changing the obj in the array likethis
{day : 16 , teamName:"liverpool"}, this is going to help you filter that array later,
then you useEffect & useState to update that array
[teams, setTeams] = useState([]);
// the array changes here {day: 1 , teamName : "sao paulo"} , {day:2 ,teamname:"liverpool"}]
useEffect(()=>{
setTeams((T)=>{
return T.filter((oneTeam)=>{
return oneTeam.teamName == selectedTeam;
});
})
},[teams,selectedTeam]);

useEffect not cleaning up after redirect

Let me explain my problem I have been solving for all day.
I have a site with header which has of course by react-router links to other pages (home, projects, about, services, contact).
Have a Project component which is in '/projects' page and '/' (home) page.
I want to make a simple animation in Project.js component which depends if there is a 'vertical' or there is not this props. Clearly -> in '/projects' I want to do that animation on scroll - in other pages not.
Tried to do that by add if statement in useEffect but it's not working, get me an error 'cannot read property 'style' of null ref.current.style.transform = `translateY(${window.scrollY * -0.35}px)`;
This problem is showing up when I am changing pages in header i.eg. I am in '/projects' scrolling and is ok animation is working then go to '/' and when scroll got error I have showed above.
It is like my if statement is not working and when I am in '/' which Project component has props vertical={false} is making animation on scroll when I don't want to do that.
What I want? I want do make an animation using useEffect only if component has a props 'vertical' like this:
Project.js component code:
const Project = ({ image, className, vertical }) => {
const ref = useRef(null);
const [isVertical, setIsVertical] = useState(vertical);
useEffect(() => {
console.log('component did mount');
isVertical
? window.addEventListener('scroll', () => {
ref.current.style.transform = `translateY(${window.scrollY * -0.35}px)`;
})
: console.log('non-vertical');
}, [isVertical]);
useEffect(() => {
return () => console.log('unmount');
});
return <StyledProject image={image} className={className} vertical={vertical} ref={ref} />;
};
in home '/':
{images.map(({ image, id }) => (
<Project key={id} image={image} />
))}
in '/projects':
{images.map(({ image, id }) => (
<StyledProject vertical image={image} key={id} />
))}
when I am in the path '/projects' and go to another path got error.
It is like after being in '/projects' it is saving all statements was but I want on every page reset useEffect and ref.current
Please help me, I can't go further since I don't fix this.
Thanks in advance.
Main problem is that you are not removing event listener when component unmounts.
Here you can see an example how to do it.

React - Leaflet MarkerCluster with Popup and Tabs

I am having an issue using the MarkerCluster leaflet component with popup and React-Tabs.
The issue is when I try to reset selected tab inside the popup, it's causing infinite loop This seems to be only when MarkerCluster group is used, otherwise it's working fine for a single marker
My code is as below
custom marker component
const ExtendedMarker = props => {
const initMarker = ref => {
if (ref && props.isOpenMarker) {
ref.leafletElement.openPopup();
}
};
return <Marker ref={initMarker} {...props} />;
};
class CustomMarker extends React.Component {
render() {
const { icon, stop, isDisabledBtn, isOpenMarker, ...props } = this.props
return (
<ExtendedMarker
icon={icon}
position={[stop.latitude, stop.longitude]}
isOpenMarker={isOpenMarker}
>
<Popup minWidth={260} closeButton={true} onOpen={() => this.setState({ tabIndex: 0 })}>
<Tabs selectedIndex={this.state.tabIndex} onSelect={tabIndex => this.setState({ tabIndex })}>
<TabList>
.
.
.
.
index.js
<MarkerClusterGroup showCoverageOnHover={false} maxClusterRadius={50}>
{currentStops.map(stop => (
<CustomMarker
key={v4()}
icon={getCategoryIconMarker(stop.category)}
stop={stop}
{...this.props}
/>
))}
</MarkerClusterGroup>
So this code works fine when MarkerClusterGroup is removed otherwise it's causing an Error: Maximum update depth exceeded
Any help would be appreciated.
Thank You
I think that is an error pattern I encountered when trying to use a function in the following way:
{getCategoryIconMarker(stop.category)}
If you instead use an arrow function, that may improve the situation. At least in my case the error disappeared. So, just replace the function above with:
{() => getCategoryIconMarker(stop.category)}
Hope someone will find it useful.

Resources