Method within React functional component - reactjs

Hello I had to add a method inside a component, that was stateless functional component. Now I am wondering if it can stay like this or should it be a class component now. My component:
const Pagination = ({ changePage }) => {
function changePageNumber(event) {
changePage(event.currentTarget.dataset.num);
}
return (
<button
className={css.button}
data-num={num}
onClick={changePageNumber}
>
{num}
</button>
);
};
Can it be like this?

Yes, you can write methods like this. Also you can use arrow functions, like:
const Pagination = ({ changePage }) => {
const changePageNumber = event => {
changePage(event.currentTarget.dataset.num);
}
return (
<button
className={css.button}
data-num={num}
onClick={changePageNumber}
>
{num}
</button>
);
};
Bonus: It's not necessary to name component that exported default, just:
export default ({ changePage }) => {
...
}
And in another file:
import AnyName from './Pagination'

You can change it to be like
const changePageNumber = (event) = () => {
changePage(event.currentTarget.dataset.num);
}
const Pagination = ({ changePage }) => {
return (
<button
className={css.button}
data-num={num}
onClick={changePageNumber}
>
{num}
</button>
);
};

changePageNumber is a function not a method. It is perfectly fine to stay there. It is there as a utility/helper function for that Pagination component. Such functions can be deployed to improve the readability of the code. Current state of your code perfectly fine.
Also you don't need to turn it into Class components, we use them when we think we need to store state, not to store methods.

Related

Like Button with Local Storage in ReactJS

I developed a Simple React Application that read an external API and now I'm trying to develop a Like Button from each item. I read a lot about localStorage and persistence, but I don't know where I'm doing wrong. Could someone help me?
1-First, the component where I put item as props. This item bring me the name of each character
<LikeButtonTest items={item.name} />
2-Then, inside component:
import React, { useState, useEffect } from 'react';
import './style.css';
const LikeButtonTest = ({items}) => {
const [isLike, setIsLike] = useState(
JSON.parse(localStorage.getItem('data', items))
);
useEffect(() => {
localStorage.setItem('data', JSON.stringify(items));
}, [isLike]);
const toggleLike = () => {
setIsLike(!isLike);
}
return(
<div>
<button
onClick={toggleLike}
className={"bt-like like-button " + (isLike ? "liked" : "")
}>
</button>
</div>
);
};
export default LikeButtonTest;
My thoughts are:
First, I receive 'items' as props
Then, I create a localStorage called 'data' and set in a variable 'isLike'
So, I make a button where I add a class that checks if is liked or not and I created a toggle that changes the state
The problem is: I need to store the names in an array after click. For now, my app is generating this:
App item view
localStorage with name of character
You're approach is almost there. The ideal case here is to define your like function in the parent component of the like button and pass the function to the button. See the example below.
const ITEMS = ['item1', 'item2']
const WrapperComponent = () => {
const likes = JSON.parse(localStorage.getItem('likes'))
const handleLike = item => {
// you have the item name here, do whatever you want with it.
const existingLikes = likes
localStorage.setItem('likes', JSON.stringify(existingLikes.push(item)))
}
return (<>
{ITEMS.map(item => <ItemComponent item={item} onLike={handleLike} liked={likes.includes(item)} />)}
</>)
}
const ItemComponent = ({ item, onLike, liked }) => {
return (
<button
onClick={() => onLike(item)}
className={liked ? 'liked' : 'not-liked'}
}>
{item}
</button>
)
}
Hope that helps!
note: not tested, but pretty standard stuff

Change state of something from another class with a button [duplicate]

I am new to React and have had some hard time to understand the concept of states.
Down below I export a stepper from MUI. I use state
export default function CustomizedSteppers() {
const steps = ['Zoninfo', 'Betalsätt', 'Börja ladda'];
const [activeStep, setActiveStep] = React.useState(0);
const handleNext = () => {
setActiveStep((activeStep+1));
};
return (
//...
//Stepper stuff...
//...
<Button variant="contained" onClick={handleNext}>Hello
World</Button>
</Stack>
);
}
I now want to split this code so that I can setActiveStep from another component.
Meaning, I want to put the button outside of this component and put it in another class, but still allow that button to change the value of activeStep - by accessing the method handleNext on click on a button outside this class. How do I manage to do this?
You need to make a parent component, define activeStep and handleNext there, and pass them to your CustomizedSteppers
Parent.js
import CustomizedSteppers from "./CustomizedSteppers"
export default function Parent() {
const steps = ['Zoninfo', 'Betalsätt', 'Börja ladda'];
const [activeStep, setActiveStep] = React.useState(0);
const handleNext = () => {
setActiveStep((activeStep+1));
};
return (
<>
<CustomizedSteppers activeStep={activeStep} handleNext={handleNext}>
<Button variant="contained" onClick={handleNext}>Hello
World</Button>
</>
)
}
CustomizedSteppers.js
export default function CustomizedSteppers({activeStep, handleNext}) {
return (
//...
//Stepper stuff...
//...
</Stack>
);
}
You have a couple options.
The first is to lift your state, like #Peter said. That is, you'll need to move activeStep to the component that houses both this component and the other component you want to have activeStep's value, and then pass that value to both (a practice known as prop drilling). This is probably the simplest way, if this is the only variable you want to do that for.
The second is to use state management tools, like #ahmetkilinc said. The Context API is native to React, so it won't require any extra installations or tools. There are also third-party tools like Redux that try to solve this problem (though I believe Redux is still just using Contexts behind the scenes, anyway). This option is better if this is a need you anticipate having multiple times throughout your application.
You can just pass the state updating function as a prop to your component.
Your Button Component:
function CustomButtonComponent (props) {
return (
<button onClick={() => props.setCount(count => count + 1)}>
Click me
</button>
)
}
export default CustomButtonComponent
And In Your Main Component
<CustomButtonComponent setCount={setCount} />
Also Since state updates are scheduled in React, you shouldnt use
onClick={() => setCount(count + 1)}
Instead use
onClick={() => setCount(prevCount => prevCount + 1)}
Try understanding following code .
const Child = (props)=>{
return (
<Button variant="contained" onClick={props.handleNext}>Hello
World</Button>
)
}
const Parent = ()=>{
const steps = ['Zoninfo', 'Betalsätt', 'Börja ladda'];
const [activeStep, setActiveStep] = React.useState(0);
const handleNext = () => {
setActiveStep((activeStep+1));
};
return (
<Child handleNext={(e)=>handleNext(e)} />
)
}
Take this code as a sample example and try implementing it with your code .
What is happening in this Parent Function ?
We are passing handleNext as a prop to to Child component which triggers handleNext function inside Parent component when a button is clicked inside Child component.

Change state of something from another class with a button

I am new to React and have had some hard time to understand the concept of states.
Down below I export a stepper from MUI. I use state
export default function CustomizedSteppers() {
const steps = ['Zoninfo', 'Betalsätt', 'Börja ladda'];
const [activeStep, setActiveStep] = React.useState(0);
const handleNext = () => {
setActiveStep((activeStep+1));
};
return (
//...
//Stepper stuff...
//...
<Button variant="contained" onClick={handleNext}>Hello
World</Button>
</Stack>
);
}
I now want to split this code so that I can setActiveStep from another component.
Meaning, I want to put the button outside of this component and put it in another class, but still allow that button to change the value of activeStep - by accessing the method handleNext on click on a button outside this class. How do I manage to do this?
You need to make a parent component, define activeStep and handleNext there, and pass them to your CustomizedSteppers
Parent.js
import CustomizedSteppers from "./CustomizedSteppers"
export default function Parent() {
const steps = ['Zoninfo', 'Betalsätt', 'Börja ladda'];
const [activeStep, setActiveStep] = React.useState(0);
const handleNext = () => {
setActiveStep((activeStep+1));
};
return (
<>
<CustomizedSteppers activeStep={activeStep} handleNext={handleNext}>
<Button variant="contained" onClick={handleNext}>Hello
World</Button>
</>
)
}
CustomizedSteppers.js
export default function CustomizedSteppers({activeStep, handleNext}) {
return (
//...
//Stepper stuff...
//...
</Stack>
);
}
You have a couple options.
The first is to lift your state, like #Peter said. That is, you'll need to move activeStep to the component that houses both this component and the other component you want to have activeStep's value, and then pass that value to both (a practice known as prop drilling). This is probably the simplest way, if this is the only variable you want to do that for.
The second is to use state management tools, like #ahmetkilinc said. The Context API is native to React, so it won't require any extra installations or tools. There are also third-party tools like Redux that try to solve this problem (though I believe Redux is still just using Contexts behind the scenes, anyway). This option is better if this is a need you anticipate having multiple times throughout your application.
You can just pass the state updating function as a prop to your component.
Your Button Component:
function CustomButtonComponent (props) {
return (
<button onClick={() => props.setCount(count => count + 1)}>
Click me
</button>
)
}
export default CustomButtonComponent
And In Your Main Component
<CustomButtonComponent setCount={setCount} />
Also Since state updates are scheduled in React, you shouldnt use
onClick={() => setCount(count + 1)}
Instead use
onClick={() => setCount(prevCount => prevCount + 1)}
Try understanding following code .
const Child = (props)=>{
return (
<Button variant="contained" onClick={props.handleNext}>Hello
World</Button>
)
}
const Parent = ()=>{
const steps = ['Zoninfo', 'Betalsätt', 'Börja ladda'];
const [activeStep, setActiveStep] = React.useState(0);
const handleNext = () => {
setActiveStep((activeStep+1));
};
return (
<Child handleNext={(e)=>handleNext(e)} />
)
}
Take this code as a sample example and try implementing it with your code .
What is happening in this Parent Function ?
We are passing handleNext as a prop to to Child component which triggers handleNext function inside Parent component when a button is clicked inside Child component.

Set React Context inside function-only component

My goal is very simple. I am just looking to set my react context from within a reusable function-only (stateless?) react component.
When this reusable function gets called it will set the context (state inside) to values i provide. The problem is of course you can't import react inside a function-only component and hence I cannot set the context throughout my app.
There's nothing really to show its a simple problem.
But just in case:
<button onCLick={() => PlaySong()}></button>
export function PlaySong() {
const {currentSong, setCurrentSong} = useContext(StoreContext) //cannot call useContext in this component
}
If i use a regular react component, i cannot call this function onClick:
export default function PlaySong() {
const {currentSong, setCurrentSong} = useContext(StoreContext) //fine
}
But:
<button onCLick={() => <PlaySong />}></button> //not an executable function
One solution: I know i can easily solve this problem by simply creating a Playbtn component and place that in every song so it plays the song. The problem with this approach is that i am using a react-player library so i cannot place a Playbtn component in there...
You're so close! You just need to define the callback inside the function component.
export const PlaySongButton = ({...props}) => {
const {setCurrentSong} = useContext(StoreContext);
const playSong = () => {
setCurrentSong("some song");
}
return (
<button
{...props}
onClick={() => playSong()}
/>
)
}
If you want greater re-usability, you can create custom hooks to consume your context. Of course where you use these still has to follow the rules of hooks.
export const useSetCurrentSong = (song) => {
const {setCurrentSong} = useContext(StoreContext);
setCurrentSong(song);
}
It is possible to trigger a hook function by rendering a component, but you cannot call a component like you are trying to do.
const PlaySong = () => {
const {setCurrentSong} = useContext(StoreContext);
useEffect( () => {
setCurrentSong("some song");
}, []
}
return null;
}
const MyComponent = () => {
const [shouldPlay, setShouldPlay] = useState(false);
return (
<>
<button onClick={() => setShouldPlay(true)}>Play</button>
{shouldPlay && <PlaySong />}
</>
)
}

Handle button click with two different handler in different condition - ReactJS

I want to handle the click of my custom button component in two separate conditions.
This is the custom-button component:
function CustomButton({status, onStop, onClick}) {
return (
// ...
<button onClick={status ? onStop : onClick}/>
// ...
)
}
This code is just a simple example to explain my question. and I can't separate my component according to the value of status.
My question is, Is this an anti-pattern or bad practice in a component? If yes, what's the best practice to do?
It's not an anti-pattern and more like a bad practice, such code isn't maintainable for when more conditions and callbacks will be added.
Components should be simple, readable and reusable while using all provided props as possible (i.e don't add unused props to component's logic):
const NOOP = () => {}
function CustomButton({ onClick = NOOP }) {
return <button onClick={onClick}>Cool Button</button>;
}
Better practice is to handle condition in parent's logic:
function Parent() {
const onContinue = // ...
const onStop = // ...
return <CustomButton onClick={status ? onStop : onContinue} />;
}
I think that is better to pass a single callback onClick and handle the business logic inside it.
Create a function handleClick inside CustomButton component before return and use if-else blocks to achieve this functionality. It is the right way, according to me.
Create your custom button and use it into any place with any props
import React from 'react';
const CustomButton = ({ children, type = 'button', onClick }) => (
<button
type={type}
onClick={onClick}
>
{children}
</button>
);
const Parent = ({ status }) => {
const stop = useCallback(() => {}, []);
const onClick = useCallback(() => {}, []);
return (
status
? <CustomButton onClick={useCallback}>stop</CustomButton>
: <CustomButton onClick={onClick}>click</CustomButton>
);
}

Resources