Is there any better way to set function to react hooks useState? - reactjs

I wanted to setState with react hooks usestate.
First I tried. but it not worked.
const [action, setAction] = useState(null);
...
<button onClick={()=>{
setAction(()=>{console.log('hi')})
}}>test</button>
Second. It worked.
const [action, setAction] = useState({action: null});
...
<button onClick={()=>{
setAction({
action:()=>{
console.log('hi')
}
})}
}>test</button>
Because if I set function directly, the state changed to undefined.
So I pass the function with object type.
But I want to know if there is another way to set state with function.

The issue with your approach is that you are not returning the function in the first place. you are just using the setAction callback to console.log('hi').
<button onClick={()=>{
setAction(()=>{console.log('hi')})
}} />
To fix this issue you need to return a new function in the callback function.
<button onClick={()=>{
setAction(() => () => {console.log('hi')})
}} />

I think what you need is useRef, not useState...!
As documented, if you passed a function to useState, it will set the state to the value it gets after executing the passed function...!
So setAction(()=>{console.log('hi')}) means set the action state to the result of executing this function ()=>{console.log('hi')}, that gives you undefined (because ()=>{console.log('hi')} returns nothing).
If you need to store a function, maybe try this...?
import { useRef } from 'react'
// ... other code
const functionRef = useRef(null)
<button
onClick={() => functionRef.current = () => console.log('hi')}
/>
// ...

I think the problem is with <button />, it should be <button></button>. Here's a JSFiddle where your code is working
const useState = React.useState;
const App = () => {
const [action, setAction] = useState(null);
return (
<button onClick={()=>{
setAction(()=>{console.log('hi')})
}}>Click me!</button>
);
}
const render = () => ReactDOM.render(
<App/>,
document.getElementById('app')
);
render();
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/0.14.0/react-dom.min.js"></script>
<div id="app">
<!-- This element's contents will be replaced with your component. -->
</div>

Related

React: Setting and updating based on props

Currently I am facing the problem that I want to change a state of a child component in React as soon as a prop is initialized or changed with a certain value. If I solve this with a simple if-query, then of course I get an infinite loop, since the components are then rendered over and over again.
Component (parent):
function App() {
const [activeSlide, setActiveSlide] = useState(0);
function changeSlide(index) {
setActiveSlide(index);
}
return (
<div className="app">
<div className="app__nav">
<Button icon="FiSun" handler={changeSlide} active={activeSlide} index="0" />
<Button icon="FiSettings" handler={changeSlide} active={activeSlide} index="1" />
</div>
</div>
);
}
Component (child):
function Button(props) {
const Icon = Icons[props.icon];
const [activeClass, setActiveClass] = useState("");
// This attempts an endless loop
if(props.active == props.index) {
setActiveClass("active");
}
function toggleView(e) {
e.preventDefault();
props.handler(props.index);
}
return(
<button className={activeClass} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
)
}
Is there a sensible and simple approach here? My idea would be to write the if-query into the return() and thus generate two different outputs, even though I would actually like to avoid this
The React docs have a nice checklist here used to determine if something does or does not belong in state. Here is the list:
Is it passed in from a parent via props? If so, it probably isn’t state.
Does it remain unchanged over time? If so, it probably isn’t state.
Can you compute it based on any other state or props in your component? If so, it isn’t state.
The active class does not meet that criteria and should instead be computed when needed instead of put in state.
return(
<button className={props.active == props.index ? 'active' : ''} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
)
This is a great use of useEffect.
instead of the if statement you can replace that with;
const {active, index} = props
useEffect(_ => {
if(active == index) {
setActiveClass("active");
}
}, [active])
The last item in the function is a dependency, so useEffect will only run if the active prop has changed.
React automatically re-renders a component when there is a change in the state or props. If you're just using activeClass to manage the className, you can move the condition in the className as like this and get rid of the state.
<button className={props.active === props.index ? 'active' : ''} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
however, if you still want to use state in the child component, you can use the useEffect hook to to update the state in the child component.
Try to use the hook useEffect to prevent the infinite loop. (https://fr.reactjs.org/docs/hooks-effect.html)
Or useCallback hook. (https://fr.reactjs.org/docs/hooks-reference.html#usecallback)
Try this and tell me if it's right for you :
function App() {
const [activeSlide, setActiveSlide] = useState(0);
const changeSlide = useCallback(() => {
setActiveSlide(index);
}, [index]);
return (
<div className="app">
<div className="app__nav">
<Button icon="FiSun" handler={changeSlide} active={activeSlide} index="0" />
<Button icon="FiSettings" handler={changeSlide} active={activeSlide} index="1" />
</div>
</div>
);
}

How do i setState onClick

Hi so i got my react function with a state that i use to know which componont should be rendered at a given time; here is my main App Function (i only include the function not the rest of class since it is not relevent)
My app function
function App(){
const [menuState, setMenuState] = useState('journal');
return(
<>
<button onClick={setMenuState('journal')}>JOUNRAL</button>
<button onClick={setMenuState('stats')}>STATS</button>
<MenuHandler menu={menuState}/>
</>
);
}
My MenuHandler function
function MenuHandler(props) {
const menu = props.menu;
if(menu==="journal")
return (<Journal />);
if(menu==="stats")
return (<Stats />);
return (<Journal />);
}
When i do this i get an infinite loop and i can't figure out why.
The problem is that you are calling setMenuState on each render, therefore the infinite loop. You are not assigning it to the onClick handler, pass an arrow function like this to solve it:
<button onClick={() => setMenuState('journal')}>JOUNRAL</button>
<button onClick={() => setMenuState('stats')}>STATS</button>

React error: Too many re-renders, when i try to conditonally render using a button

This happens when i try to conditonally render using a button, what should i do?
This is the main function:
const hi = () =>
{
if (qajo)
{
return (
<>
Email
Phone
</>
)
}
}
This is the main return:
return (
<>
<a href="#" className="btn btn-dark" onClick={() => setQajo(true)}>Request a quote</a>
{hi}
</>
)
I have a useState variable at the start, called qajo:
const [qajo, setQajo] = useState()
setQajo(false)
When you call setQajo state of component change and React try to recreate them.. but when component is recreated setQajo change state again.. never ending loop. Set initial state in this way:
const [ qajo, setQajo ] = useState(false)

Unexpected behaviour with rendering a list of buttons in react

I am trying to generate a list of buttons using this method. I rewrote it as a test instance and the behaviour is the same - when you render the buttons, the function in onClick is called and cannot be called again by clicking the generated buttons. In this case 1 to 5 are logged.
function App() {
const [buttonList, setButtonList] = useState();
const experimental = async (e) => {
const array = [1,2,3,4,5];
const listItems = array.map((item) =>
<li key={item}>
<button onClick={console.log(item)}>This is a test</button>
</li>
);
setButtonList(listItems);
}
return (
<div className="App">
<header className="App-header">
<button onClick={experimental}>Click to render</button>
<ul>{buttonList}</ul>
</header>
</div>
);
}
Could anyone explain this behaviour to me and/or offer a way of doing this which doesn't do this?
Due to context this. It's must be () => {}. Let's use function and useCallback hook instead.

Update props of react element rendered from variable

there is a react component stored inside of a state variable. I've attached a sample code and created a codesandbox sample. As you can see, nameBadgeComponent is supposed to display {name} field from state. It works fine for the default value, but does not react to changes in name variable. Is there a way to make it work without updating the nameBadgeComponent itself?
const [name, setName] = useState("DefaultName");
const [nameBadgeComponent] = useState(<h2>My name is: {name}</h2>);
return (
<div className="App">
{nameBadgeComponent}
<button
onClick={() => {
const newName = Math.random()
.toString(36)
.substring(3);
console.log("Renamed to:", newName);
setName(newName);
}}
>
rename
</button>
</div>
);
You can't create a component from a useState. A state is all the properties and data that define in what condition/appearance/etc the component is. A useState allows to control a piece of information or data, but doesn't respond to changes, a component does.
The fact that you put "Component" in the name should give you a hint ;)
What you want to do is probably this :
function NameBadgeComponent({ name }) {
return <h2>My name is: {name}</h2>;
}
export default function App() {
const [name, setName] = useState("DefaultName");
return (
<div className="App">
<NameBadgeComponent name={name} />
<button
onClick={() => {
const newName = Math.random()
.toString(36)
.substring(3);
console.log("Renamed to:", newName);
setName(newName);
}}
>
rename
</button>
</div>
);
}
This will update to the changes in name properly.
Why are you extracting the nameBadgeComponent to a state? That is useless as it only relies on name, keeping your code as close to the original as possible:
const [name, setName] = useState("DefaultName");
const nameBadgeComponent = <h2>My name is: {name}</h2>;
return (
<div className="App">
{nameBadgeComponent}
<button
onClick={() => {
const newName = Math.random()
.toString(36)
.substring(3);
console.log("Renamed to:", newName);
setName(newName);
}}
>
rename
</button>
</div>
);
}
Context as to why it does not work: if you use useState you are defining the starting state, the code in there is never run again unless you use setState (in this case setNameBadgeComponent)
Therefore its never updated
EDIT:
The other answer defining the extra component is more reactish code, if you are going to accept an answer accept that one.

Resources