Changing states in next js 13 - javascript

Recently I started learning nextjs (13) and have this problem with navbar. The problem is that when I move through navbar to other section it's not affect the state and not adding class to that Link component so that the border of picked section display (that means we are on that section). When click second time on the same section it shows. I think it's all about server-side-rendering ofc.
const [selected, setSelected] = useState();
const click = (option) => {
setSelected(option);
};
return (
<div className="flex text-center flex-row navbar font-mono">
<div className="navLink">
<Link
onClick={() => click("popular")}
className={`option ${selected === "popular" ? "clicked" : ""} p-1`}
href={`/most_popular`}
>
Most Popular
</Link>
</div>
<div className="navLink">
<Link
onClick={() => click("rated")}
className={`option ${selected === "rated" ? "clicked" : ""} p-1`}
href={`/top_rated`}
>
Top Rated
</Link>
</div>
<div className="navLink">
<Link
onClick={() => click("upcoming")}
className={`option ${selected === "upcoming" ? "clicked" : ""} p-1`}
href={`/upcoming`}
>
Upcoming
</Link>
</div>
</div>
);
};
I am doing this simple, but what we have after clicking on specify Link it shows the server generated page, which is navBar without class clicked.
I was trying to use such a hooks like useRouter (next/navigation instead next/router), but we cannot get the current pathname now from it.

Related

How can I change the language of the application manually with a button in NextJS?

I am using next-translate to internationalize a web page. I want to translate the page depending on the language selected by the user, but I have some problems implementing it. The main problem is that when changing the "locale", the link is changed and an attempt is made to access one that does not exist. I leave you code:
Navbar.tsx
const Navbar = ({ children }: any) => {
const router = useRouter();
const { t } = useTranslation();
return (
<div style={{ position: "sticky" }}>
<nav className={styles.container}>
<div className={styles.links_container}>
<div
className={`${router.pathname === "/" ? styles.active_link : ""} ${
styles.link
}`}
>
<Link href="/">
<p>{t("navigation:home")}</p>
</Link>
</div>
<div
className={`${
router.pathname === "/expertices" ? styles.active_link : ""
} ${styles.link}`}
>
<Link href="/expertices">
<p>{t("navigation:expertices")}</p>
</Link>
</div>
<div
className={`${
router.pathname === "/about" ? styles.active_link : ""
} ${styles.link}`}
>
<Link href="/about">
<p>{t("navigation:about")}</p>
</Link>
</div>
<div
className={`${
router.pathname === "/contact" ? styles.active_link : ""
} ${styles.link}`}
>
<Link href="/contact">
<p>{t("navigation:contact")}</p>
</Link>
</div>
// Changes locale
<Link href="/" locale="fr">
<h2>Français</h2>
</Link>
<Link href="/" locale="es">
<h2>Español</h2>
</Link>
</div>
</nav>
{children}
</div>
);
};
When clicking on the Link that changes the locale, the URL becomes http://localhost:3000/fr and I get a 404 error. What I want is to change the language of the application and collect the information of another folder, not change the url

Conditional styling in react map

I only want to show the display block on the hovered item. but when I hover it shows on every item in a map function. what I'm doing wrong.
basically, I just want to show hovered movie item's title. for now, it shows every movie when I hover.
MovieList.js
const [popular, setPopular] = useState([]);
const [hover, setHover] = useState(false);
return (
<>
<div className="movie-list">
<h2 style={{ fontSize: "49px", marginLeft: "60px" }}>What's Popular</h2>
<div className="popular">
{popular.map((pop, index) => (
<>
<div className="movie" key={index}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4
id="pop-title"
style={{ display: hover ? "block" : "none" }}
key={index}
>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</>
))}
</div>
</div>
</>
);
};
export default MovieList;
The same hover state variable is used for all your movies, so when it becomes true, all the movies are affected by the change. You could use something like an object instead of just a boolean to store one hover state per movie.
Another problem with your code that isn't helping:
You are missing a unique key prop on each item of the map (it must be on the direct child).
Solution 1: Remove the Fragment
Everything is already under one div so you don't need the React Fragment (<>) in that case. Also, you might wanna use something unique to the current map item other than the index in the array.
{popular.map((pop) => (
<div className="movie" key={pop.somethingUnique}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
))}
Solution 2: Set the key on the Fragment
{popular.map((pop) => (
<Fragment key={pop.somethingUnique}>
<div className="movie">
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</Fragment>
))}

How to get parent div in handleChange with React

I have multiple divs that are dynamic (it depends on which filter the visitor checked). So I can not use getElementById.
I need to get the parent div to change its CSS if an input is checked.
Here is my code:
{workout?.map(x => {
return <div className='relative bg-gray-200 p-4 rounded-md my-3 mx-2' key={x.name}>
<input onClick={handleChecked} className='absolute right-0 top-0' type="checkbox" />
<div className='flex'>
<img className='w-2/6 rounded mr-5' src={`${x.path}`} alt={x.name} />
<div className='w-4/6'>
<h2 className='text-xl'>{x.name}</h2>
<p>Nombre de séries : {x.set}</p>
<p>Nombre de rép : {x.reps}</p>
{x.secondary ? <p>Muscles solicités : <br />
<span className='space-x-2'>
{x?.secondary?.map(k => {
return <span className='bg-white px-1 rounded-md' key={k}>{k}</span>
})}
</span>
</p> : null}
</div>
</div>
</div>
})}
The idea, is to add a border-2 border-teal-500 class to the parent div of the input when it is checked.
Here is my handleChecked:
function handleChecked(e) {
// code here
}
I saw that I had to use parentNode but the problem is, I can't store in a variable the current input because it is dynamic. Every item has its own input.
Any idea how I can handle this?
You shouldn't be using getElementById (or any other vanilla JS DOM method) in React anyway - store and change values in state instead. You can leverage this by making input a controlled component, and storing the checked values in an array, or in workout - then, when returning the JSX, you just need to check whether the x variable (the item being iterated over) indicates that the current input should be checked or not - which will also tell you whether the parent should have a different class.
const makeHandleChecked = (i) => (e) = {
setWorkout(
workout.map(
(w, j) => j !== i ? w : { ...w, checked: e.target.checked }
)
);
};
{workout?.map((x, i) => (
<div className={`relative bg-gray-200 p-4 rounded-md my-3 mx-2${x.checked ? ' checked' : ''}`} key={x.name}>
<input onClick={makeHandleChecked(i)} checked={x.checked} className='absolute right-0 top-0' type="checkbox" />

React - loading the page calls all onClicks at once, then clicking on a button does nothing

I'm quite new to react, I have a good understanding of HTML and CSS/SASS so I've not run into any problems until now.
I want to highlight a button on the nav bar depending on which page the user is on at the time.
I've tried to add a simple onClick to the buttons that will call a function, passing in a string telling it that "home" has just been clicked or "contact" for example.
I've added an alert() to the function so I can see that the buttons been pressed and method is being called;
However, when the page is loaded up, it calls every buttons method and gives me an alert for every button, then continues to load up as usual and clicking the buttons seems to be as if I never added an onClick in the first place.
Can anybody tell me where I am going wrong with this simple function? I've spent the past 5 hours pulling my hair out.
Thank you!
NavBar.js:
function NavBar() {
const isActive = "home";
return (
<nav className="landing-page__nav-bar nav-bar">
<ul className="nav-bar__list">
<Link to ='/home'><li><a data-page="home" className="home-link">
<button href="landingpage" className={` ${isActive === "home" ? 'btn__nav-bar-btn active-link' : 'btn__nav-bar-btn'}`} onClick={buttonWasClicked("home")}>Home</button>
</a></li>
</Link>
<Link to ='/portfolio'><li><a data-page="portfolio" className="portfolio-link">
<button className={` ${isActive === "portfolio" ? 'btn__nav-bar-btn active-link' : 'btn__nav-bar-btn'}`} onClick={buttonWasClicked("portfolio")}>Portfolio</button>
</a></li>
</Link>
<Link to ='/artwork'><li><a data-page="doodles" className="doodles-link">
<button className={` ${isActive === "artwork" ? 'btn__nav-bar-btn active-link' : 'btn__nav-bar-btn'}`} onClick={buttonWasClicked("artwork")}>Artwork</button>
</a></li></Link>
<Link to ='/photography'><li><a data-page="photography" className="photography-link">
<button className={` ${isActive === "photography" ? 'btn__nav-bar-btn active-link' : 'btn__nav-bar-btn'}`} onClick={buttonWasClicked("photography")}>Photography</button>
</a></li></Link>
<Link to ='/cv'><li><a data-page="cv" className="cv-link">
<button className={` ${isActive === "cv" ? 'btn__nav-bar-btn active-link' : 'btn__nav-bar-btn'}`} onClick={buttonWasClicked("cv")}>CV</button
></a></li></Link>
<Link to ='/about'><li><a data-page="about" className="about-link">
<button className={` ${isActive === "about" ? 'btn__nav-basr-btn active-link' : 'btn__nav-bar-btn'}`} onClick={buttonWasClicked("about")}>About</button>
</a></li></Link>
<Link to ='/contact'><li><a data-page="contact" className="contact-link">
<button className={` ${isActive === "contact" ? 'btn__nav-bar-btn active-link' : 'btn__nav-bar-btn'}`} onClick={buttonWasClicked("contact")}>Contact</button>
</a></li></Link>
</ul>
</nav>
);
}
export default NavBar;
ButtonClick.js (method to update the current page):
import NavBar from './Nav-bar';
function ChangeActiveButton(selectedButton) {
alert(selectedButton)
NavBar.isActive = toString(selectedButton);
}
export default ChangeActiveButton;
You're calling the function when rendering and passing the result of that function to the button's onClick. Instead, you should give the onClick an anonymous function that when executed calls the function you want with the proper parameter.
<button className={` ${isActive === "about" ? 'btn__nav-basr-btn active-link' : 'btn__nav-bar-btn'}`} onClick={() => buttonWasClicked("about")}>About</button>
onClick tag requires a function, not to call a function. If you type buttonWasClicked("contact") the function will be called when the DOM is ready.
Instead you need to write {() => buttonWasClicked("contact")}

How to set the onBlur correctly for a dropdown in React js

I am having a dropdown list with certain years and showing the current year by default. I want to set an onBlur event to close the dropdown when the user clicks outside. onBlur works fine but my problem is I can't set the currently selected year. the problem here is that when I select a value from the dropdown which is the child of this div, it triggers the onBlur event hence dropdown closes and I can't set value. now, how do I achieve setting onBlur correctly so that selecting elements from the list doesn't trigger onBlur or is there any other way to achieve this.
this is Child component
import React from 'react';
const SelectProjection = (props) => {
let years;
if (props.yearList) {
years = props.yearList.map((year, index) => {
return (
<div
className="dropdown-child"
title="value"
key={index}
role="presentation"
onClick={(e) => props.filterHandler(year)}
tabIndex={0}
>
<span id="dropdown-child-item">{year}</span>
</div>
);
});
} else {
years = <div>Loading....</div>;
}
return (
<div>
<div
id="sort-by"
className={`${props.orderBy} ${
props.open ? 'open' : ' close'
}`}
>
<div className="header" style={{ fontSize: '12px' }}>
Project to year
</div>
<div className="sort-by-dropdown" onBlur={props.hideDropdown} >
<div
className="selected"
id="openProjectionFilter"
onClick={props.showDrop}
onKeyPress={props.showDrop}
tabIndex={0}
role="button"
>
<span id="selected-sort-item">{props.selectedYear} </span>
</div>
<div className="options">
<div
className="dropdown-child"
title="value"
role="presentation"
tabIndex={1}
onClick={() => props.filterHandler()}
>
<span id="dropdown-child-item">{props.selectedYear}</span>
</div>
{years}
</div>
</div>
</div>
</div>
);
}
export default SelectProjection;
this is my parent component
class HealthFilter extends React.Component {
state = { openProjectionFilter: false };
showProjectionList = () => {
this.setState(state => ({
openProjectionFilter:!state.openProjectionFilter }));
};
filterYearHandler = (year) => {
console.log('child clicked')
this.props.handlerYear(year)
this.setState({ openProjectionFilter: false })
}
hideDropdown = (event) => {
console.log('parent clicked')
this.setState({ [event.target.children[0].id]: false })
}
render() {
const { openProjectionFilter } = this.state;
const { boms } = this.props
return (
<div id={'filter'}>
<div id="alert-filter-body" className=''>
<SelectProjection
open={openProjectionFilter}
hideDropdown={this.hideDropdown}
showDrop={this.showProjectionList}
selectedYear={this.props.selectedYear}
filterHandler={this.filterYearHandler}
yearList={this.props.yearList}
/>
<div className="checkboxes" style={{ marginTop: 10 }}>
<div className="filter-button green" role="button" onClick={this.props.submitProjections}>
Calculate
</div>
</div>
</div>
</div>
);
}
}
HealthFilter.propTypes = {};
export default HealthFilter;
because class .options is not in class .selected. the solution is you move onBlur={props.hideDropdown} to the parent of both.
<div className="sort-by-dropdown" onBlur={props.hideDropdown}>
Since React v.17 the React onBlur / onFocus events use the native events focusin / focusout - which means, when using onBlur / onFocus with React 17+ those events will prpegate. (Which is pretty useful for dropdowns).
You can now just wrap input element and the dropdown element in one container div and set the focusevents on that container - and since focus events now propegate they will bubble from the input aswell as from the dropdown to the div (so clicking the dropdown will not trigger an onBlur event).
For a more indepth explenation see my answer here: https://stackoverflow.com/a/73922116/19529102 (It's a plain JS and HTML answer - no react - but that doen't really matter, since React already uses the propagating focus events (focusin and focusout)
Use this dropdown component this work correctly, it detects the on blur and closes the dropdown.
import React, { useState } from "react";
const TableDropdown = () => {
const [state, setState] = useState('hidden');
const handleOpenDropdown = (event) => {
setState('block');
}
const handleCloseDropdown = (event) => {
if (!event.currentTarget.contains(event.relatedTarget)) {
setState('hidden');
}
}
return (
<React.Fragment>
<div className="relative" onBlur={(event) => handleCloseDropdown(event)}>
<div>
<button type="button" onClick={(event) => handleOpenDropdown(event)} className="inline-flex w-fit justify-center itce rounded border bg-ascent text-white px-4 py-2 text-xs font-medium">Options</button>
</div>
<div className={`absolute ${state} border right-0 z-10 mt-2 w-fit origin-top-right rounded bg-slate-50 shadow-lg px-2 py-3`}>
<ul className="flex flex-col space-y-3">
<li>Account Information</li>
<li>Account Information</li>
<li>Account Information</li>
<li>Account Information</li>
</ul>
</div>
</div>
</React.Fragment>
);
}
export default TableDropdown;

Categories

Resources