Button onClick not firing in Reactjs - javascript

Buttons are not doing what they are supposed to do, adding to good, neutral, bad feedback.
Curiously, I added a default button that uses different syntax to add to bad, and it works. So there is something wrong with my "giveGoodFeedback" functions.
import React, { useState } from 'react'
const Button = (props) => {
return (
<button onClick={props.handleClick}>
{props.text}
</button>)
}
const App = () => {
// save clicks of each button to its own state
const [good, setGood] = useState(0)
const [neutral, setNeutral] = useState(0)
const [bad, setBad] = useState(0)
const giveGoodFeedback = () => {
setGood( good + 1 )
}
const giveNeutralFeedback = () => {
setNeutral( neutral + 1 )
}
const giveBadFeedback = () => {
setBad( bad + 1 )
console.log('Bad increases')
}
return (
<div>
<h1>give feedback</h1>
<Button text='good' onClick={giveGoodFeedback}/>
<Button text='bad' onClick={giveBadFeedback}/>
<Button text='neutral' onClick={giveNeutralFeedback}/>
<button onClick={() => setBad(bad + 1)}>
Click me
</button>
<h1>Statistics</h1>
<ul>
<li>good : {good}</li>
<li>neutral : {neutral}</li>
<li>bad : {bad}</li>
</ul>
code here
</div>
)
}
export default App;

Because you are using incorrect props, it should be props.onClick , please have a try. Thanks me later👍

Related

Adding a Class and Setting States Within The Same Function In React [duplicate]

I have some buttons and I'm trying to add active class for clicked button. But when I click one of the buttons, all buttons are getting active class.
const { useState } = React;
const { render } = ReactDOM;
const node = document.getElementById("root");
const Button = ({ message }) => {
const [condition, setCondition] = useState(false);
return (
<div>
{
Object.keys(res).map((data) => (
<Button className={condition ? "button toggled" : "button"} onClick=.
{() => {
setCondition(!condition)}
}}
))
}
</div>
);
//Updated
Object.keys(res).map((data) => (
<Button className={condition ? "button toggled" : "button"} onClick=.
{() => {
setCondition(condition === "off" ? "on" : "off")}
}}
))
}
</div>
); //This can be modified to work for button clicked. Because active class is added to all buttons, if one of them is clicked
};
render(<Button message="Click me if you dare!" />, node);
This is working if I click the first button, but if I click again the same button, this active class should be removed
Here is a very naive solution, but it will help you understand the problem.
If you're on a real project, I suggest you to use an existing library (that can be found by searching react toggle button group)
import React, {useState} from "react";
const defaultButtons = [
{id: 1},
{id: 2},
{id: 3},
{id: 4}
];
export default function App() {
const [toggledButtonId, setToggledButtonId] = useState(null);
function toggleButton(button) {
setToggledButtonId(button.id);
}
return (
<div>
{defaultButtons.map(button => {
const isToggled = button.id === toggledButtonId;
return (
<button
key={button.id}
className={isToggled ? "toggledButtonId toggled" : "toggledButtonId"}
onClick={() => toggleButton(button)}>
{String(isToggled)}
</button>
)
})}
</div>
)
}
import React, {useState, useCallback} from "react";
const defaultButtons = [
{id: 1},
{id: 2},
{id: 3},
{id: 4}
];
export default function App() {
const [toggledButtonId, setToggledButtonId] = useState(false);
function toggleButton(button) {
setToggledButtonId(button.id);
}
const toggleButton = useCallback((id) => setToggledButtonId(state => id), [toggledButtonId]);
return (
<div>
{defaultButtons.map(button => {
const isToggled = button.id === toggledButtonId;
return (
<button
key={button.id}
className={isToggled ? "toggledButtonId toggled" : "toggledButtonId"}
onClick={toggleButton(button.id)}>
{String(isToggled)}
</button>
)
})}
</div>
)
}
You need to use a seperate state handler for each button:
const Button = ({ message }) => {
const [condition, setCondition] = useState(false);
const [condition2, setCondition2] = useState(false);
return (
<div>
<div
onClick={() => setCondition(!condition)}
className={condition ? "button toggled" : "button"}
>
{message}
</div>
<div
onClick={() => setCondition(!condition2)}
className={condition2 ? "button toggled" : "button"}
>
{message}
</div>
</div>
);
};
render(<Button message="Click me if you dare!" />, node);
maybe you want to check this.
https://codesandbox.io/embed/import-css-file-react-vs488?fontsize=14&hidenavigation=1&theme=dark
You could create component Button with state within and use this component to populate buttons. Probably you can use :active CSS selector and avoid js at all

disable another button on react js

I have a React project which has three buttons in it. When I click a button, For example I click the second button, it will log "this is from button two ". followed by a random number with time interval 500ms. My aim is when I click a button, another buttons will be disabled, how to do this? Thanks in advance.
This is my App.js
import './App.css';
import { Button1 } from './Button1.React';
import { Button2 } from './Button2.React';
import { Button3 } from './Button3.React';
function App() {
return (
<div className="App">
<Button1>log one</Button1>
<Button2>log two</Button2>
<Button3>log three</Button3>
</div>
);
}
export default App;
this is my Button1.React.jsx
import React from "react";
export const Button1 = ({
children,
type,
doStuff1 = ()=> {
console.log("this is button one ", Math.random().toFixed(1));
setTimeout(doStuff1, 500);
},
})=>{
return (
<button onClick={doStuff1} type={type}>
{children}
</button>
)
};
this is my Button2.React.jsx
import React from "react";
export const Button2 = ({
children,
type,
doStuff2 = ()=> {
console.log("this is button two ", Math.random().toFixed(1));
setTimeout(doStuff2, 500);
}
})=>{
return (
<button onClick={doStuff2}type={type}>
{children}
</button>
)
};
this is my Button3.React.jsx
import React, { Children } from "react";
export const Button3 = ({
children,
type,
doStuff3 = ()=> {
console.log("this is button three ", Math.random().toFixed(1));
setTimeout(doStuff3, 500);
}
})=>{
return (
<button onClick={doStuff3}type={type}>
{children}
</button>
)
};
and here's the screenshot from the rendered page
i've been told to do this is using useState that returns a boolean condition to disable/enable click, but i don't know how to do this.
function App() {
const [bool1, setBool1] = useState(false);
const [bool2, setBool2] = useState(false);
const onClickBtn1 = () => {
// your other codes when btn 1 is clicked
// ------
setBool2(true); //disable button 2
}
const onClickBtn2 = () => {
// your codes when btn 2 is clicked
// ------
setBool1(true); //disable button 1
}
return (
<div className="App">
<button onClick={onClickBtn1} disabled={bool1}>Button 1</button>
<button onClick={onClickBtn2} disabled={bool2}>Button 2</button>
</div>
);
}
Basically, you use disabled in the button element, the value of the disabled should be in the state so the app will re-render if its value changes. Finally, change the value of the state on the button's onClick handler. You need to learn to use React Hooks for this since you said you don't know useState.
Is this what you try to do
import React, { useState } from "react";
const Button = ({ children, type, disabled, doStuff }) => {
return (
<button onClick={doStuff} type={type} disabled={disabled}>
{children}
</button>
);
};
export default function Name() {
const [activeButton, setActiveButton] = useState();
const disableOtherButtons = (currentButton) => {
if (activeButton === currentButton) {
setActiveButton()
} else {
setActiveButton(currentButton)
}
}
const doStuff1 = () => {
console.log("this is button one ", Math.random().toFixed(1));
disableOtherButtons('button1');
};
const doStuff2 = () => {
console.log("this is button one ", Math.random().toFixed(1));
disableOtherButtons("button2");
};
const doStuff3 = () => {
console.log("this is button one ", Math.random().toFixed(1));
disableOtherButtons("button3");
};
return (
<div>
<Button
doStuff={doStuff1}
disabled={activeButton && activeButton !== "button1"}
>
Button1
</Button>
<Button
doStuff={doStuff2}
disabled={activeButton && activeButton !== "button2"}
>
Button2
</Button>
<Button
doStuff={doStuff3}
disabled={activeButton && activeButton !== "button3"}
>
Button2
</Button>
</div>
);
}
You can pass reference useState to buttons
import { useRef } from 'react';
import './App.css';
import { Button1 } from './Button1.React';
import { Button2 } from './Button2.React';
import { Button3 } from './Button3.React';
function App() {
const [ btn1, setBtn1 ] = useState(false) // by default disabled will be set to false
const [ btn2, setBtn3 ] = useState(false)
const [ btn3, setBtn3 ] = useState(false)
const actions = [ setBtn1, setBtn2, setBtn3 ]
return (
<div className="App">
<Button1 actions={actions} disabled={btn1}>log one</Button1>
<Button2 actions={actions} disabled={btn2}>log two</Button2>
<Button3 actions={actions} disabled={btn3}>log three</Button3>
</div>
);
}
export default App;
and for buttons you need to accept those parameters and work with them
import React from "react";
export const Button1 = (props) => {
const { children, type, actions, disabled } = props
const doStuff = () => {
actions[0](false); // this will set Button1 to disabled
console.log("this is button three ", Math.random().toFixed(1));
setTimeout(doStuff3, 500);
}
return (
<button onClick={doStuff} type={type} disabled={disabled}>
{children}
</button>
)
};
I can see you do setTimeout(doStuff3, 500); inside of function that is referencing undefined function, you might want to define those in App.js and pass those as props to buttons

How to re-fetch data after doing a filter in React.js with useContext

I got this component in React.js which make different kinds of filtering when I click a button, this is my code:
import React, { useContext } from 'react';
import { ModelsContext } from "../context/ModelsContext";
const FilterNav = () => {
const { modelos, guardarModelo } = useContext(ModelsContext);
const filterSegment = e => {
const segment = modelos.filter(modelo => modelo.segment === e.target.name);
guardarModelo(segment);
}
return (
<nav className="filter-container">
<div className="container">
<h3 className="filter-element-title">Filtrar por</h3>
<button type="button" className="filter-element">Todos</button>
<button type="button" className="filter-element" name="Autos" onClick={filterSegment}>Autos</button>
<button type="button" className="filter-element" name="Pickups y Comerciales" onClick={filterSegment}>Pickups y Comerciales</button>
<button type="button" className="filter-element" name="SUVs y Crossovers" onClick={filterSegment}>SUVs y Crossovers</button>
</div>
<p className="filter-element-last">Ordenar por ^</p>
</nav>
);
}
export default FilterNav;
The information I get from the api with useContext in ModelsContext.jsx, here is what I wrote so far:
import React, { createContext, useState, useEffect } from 'react';
export const ModelsContext = createContext();
const ModelsProvider = (props) => {
//State de modelos
const [modelos, guardarModelo] = useState([]);
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
guardarModelo(modelos);
}
//Cargar un modelo
useEffect(() => {
consultarAPI()
}, []);
return (
<ModelsContext.Provider
value={{
modelos,
guardarModelo
}}
>
{props.children}
</ModelsContext.Provider>
)
}
export default ModelsProvider;
My issue is that when I filter the API modelos throught the filterSegment function I don't know how to re-fetch the data from the API, because when I do a new call to the filterSegment function it filters the filtered data. I've tried to add a boolean state, and I was thinking about adding another state with allthedata, but I really lost about implementing it, I'm still very new to React.js.
I've search through stack overflow and google and I cannot get the answer, If you can give me a clue or some sort of guidance it will be appreciated.
Thanks so much!
You can add another state in the ModelsContext:
//State de modelos
const [modelos, guardarModelo] = useState([]);
const [allModelos, guardarAllModelo] = useState([]);
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
guardarAllModelo(modelos);
//uncomment if you want to have initial value for modelos state
//guardarModelo(modelos);
}
// some codes ...
<ModelsContext.Provider
value={{
allModelos,
modelos,
guardarModelo
}}
>
{props.children}
</ModelsContext.Provider>
Then in the FilterNav component:
const {allModelos, modelos, guardarModelo } = useContext(ModelsContext);
const filterSegment = e => {
const segment = allModelos.filter(modelo => modelo.segment === e.target.name);
guardarModelo(segment);
}
But this does not really re-fetch data from your web api. It just re-filters the first fetched data. if you want to re-fetch data from web api you can add consultarAPI in your context provider then call it somewhere.
Thanks code is working
This is my Portfolio gallery code First time load all data when click category then get category dataenter code here
Thanks code is working
This is my Portfolio gallery code First time load all data when click category then get category data`enter code here`
import React, { Component, useEffect, useState } from 'react'`enter code here`;
import Thumnailport_list from './Thumnailport_list';
import Portlightbox from './Portlightbox';
import Functional from './Functional';
import $ from 'jquery';
const Portfolio = () => {
const filterItem = async (categoryitem) => {
const updateitmes = allModelos.filter((curElm) => {
return curElm.categories === categoryitem
})
getporfolioState(updateitmes)
}
const [getporfolio, getporfolioState] = useState([])
const [allModelos, guardarAllModelo] = useState([]);
$(document).ready(function () {
$(".grid-wrap .grid li").unbind().click(function (e) {
console.log(this.className);
var newe = this.className;
$('.' + newe).addClass('current show');
$("#grid-gallery").addClass("slideshow-open");
});
$("#closeport").unbind().click(function (e) {
$("#grid-gallery").removeClass("slideshow-open");
$(".portfolio .grid li").removeClass('current show');
$(".portfolio .slideshow ul > li").removeClass('current show');
});
});
const portadd = () => {
document.body.classList.add('portfolio');
document.body.classList.add('at-top');
document.getElementById('port').classList.add('no-transform');
document.getElementById('port').classList.add('revealator-within');
document.getElementById('port2').classList.add('no-transform');
document.getElementById('port2').classList.add('revealator-within');
document.getElementById('navbar-collapse-toggle').classList.remove('biohidemenu');
}
const getalldata = async () => {
try {
const res = await fetch("/getdata", {
method: 'Get',
headers: {
'Content-Type': 'application/json'
}
})
const data = await res.json()
// console.log("This is our data load")
// console.log(data.portfolio)
getporfolioState(data.portfolio)
guardarAllModelo(data.portfolio)
} catch (error) {
console.log(error)
// history.push("/backoffice/login")
}
}
useEffect(() => {
getalldata()
portadd()
}, []);
return (
<>
<section id="port" class="title-section text-left text-sm-center revealator-slideup revealator-once revealator-delay1">
<h1 >my <span>portfolio</span></h1>
<span class="title-bg">works</span>
</section>
<section id="port2" className="main-content text-center revealator-slideup revealator-once revealator-delay1">
<div class="container">
<button className="btn btn-about " onClick={() => filterItem('mobileapp')}>Mobile</button>
<button className="btn btn-about " onClick={() => filterItem('frontend')}>Frontend</button>
<button className="btn btn-about " onClick={() => filterItem('gdesign')}>Graphics</button>
</div>
<div id="grid-gallery" className="container grid-gallery">
{/* Portfolio Grid Starts */}
<section className="grid-wrap">
<ul className="row grid">
{
getporfolio.map((getdata, index) => {
return (
<>
<Thumnailport_list
key={index}
portID={getdata._id}
imagetag={getdata.imguploadedFile}
figuertext={getdata.projectname}
/>
</>
)
})
}
</ul>
</section>
{/* Portfolio Grid Ends */}
{/* Portfolio Details Starts */}
<section className="slideshow" id="sdfer">
<ul>
{/* Portfolio Item Detail Starts */}
{
getporfolio.map((getdata, index) => {
return (
<>
<Portlightbox
idlight={getdata._id}
imagelight={getdata.imguploadedFile}
langport={getdata.language}
clientport={getdata.client}
projectnameport={getdata.projectname}
previewport={getdata.preview}
/>
</>
)
})
}
</ul>
{/* Portfolio Navigation Starts */}
<nav>
{/*<span className="icon nav-prev prev"><img src="images/left-arrow.png" alt="previous" /></span>
<span className="icon nav-next next"><img src="images/right-arrow.png" alt="next" /></span>*/}
<span className="nav-close" id="closeport"><img src="images/close-button.png" alt="close" /> </span>
</nav>
{/* Portfolio Navigation Ends */}
</section>
</div>
</section>
</>
)
}
export default Portfolio;

How to send values React Hook(props)

I made My code.
When Click the button A, appear AAA.
Or Click the button B, appear BBB, Click the button C, appear CCC.
// Main > RightMain.js
import React, { useState } from 'react'
function RightMain() {
const [screen, setScreen] = useState('');
const A = () => {
setScreen('A')
}
const B = () => {
setScreen('B')
}
const C = () => {
setScreen('C')
}
return (
<div>
<button onClick={A}>A</button>
<button onClick={B}>B</button>
<button onClick={C}>C</button>
{screen === 'A' && <div>AAA</div>}
{screen === 'B' && <div>BBB</div>}
{screen === 'C' && <div>CCC</div>}
</div>
)
}
export default RightMain
And I wanna separate My Code(RightMain.js).
When I Click the Button on the RightMain.js.
The Result appear's on the Formations.js like the image below.
But I don kno how to bring value(RightMain.js's screen) to the Formations.js.
// Main > LeftMain.js
import React from 'react'
import RadioBtn from './LeftMain/RadioBtn';
import Formation from './LeftMain/Formation';
function LeftMain() {
return (
<div>
<div>
<RadioBtn />
</div>
<div>
<Formation />
</div>
</div>
)
}
export default LeftMain
//Main > LeftMain > Formation.js
import React, { useState } from 'react'
import RightMain from '../RightMain';
function Formation() {
return (
<div>
</div>
)
}
export default Formation
Thx
If I understand correctly, LeftMain and RightMain are sibilings, and Formation is a child of LeftMain.
One possible approach is to use Context API.
Something like this should work:
// Define the default value
// or return null and take that into consideration when using "useContext"
export const MyCurrentScreenContext = React.createContext({
setScreen: () => void 0,
screen: ''
});
export const MyCurrentScreenProvider = props => {
const [screen, setScreen] = useState('');
const value = useMemo(() => ({ screen, setScreen }), [screen, setScreen]);
return (
<MyCurrentScreenContext.Provider value={value}>
{props.children}
</MyCurrentScreenContext.Provider>
);
}
const Main = () => {
...
return (
<MyCurrentScreenProvider>
<LeftMain />
<RightMain />
...
</MyCurrentScreenProvider>
);
}
const RightMain() {
const { setScreen } = useContext(MyCurrentScreenContext);
....
};
const Formation() {
const { screen } = useContext(MyCurrentScreenContext);
....
};
Read more about context api at the official docs
From what I understand, you want to pass the values down to the child components. If that is correct then you could pass them as parameters when calling it and using props to receive them inside the child component. Something like this.
<div>
<RadioBtn randomVal="value" />
</div>

react not rerendering after state change

I know there have been similar questions, but I have a weird issue.
This is what I'm doing
import React, {useState} from 'react';
import './App.css';
import {Table, Button, InputGroup, FormControl} from 'react-bootstrap';
function App() {
const [pons, setPons] = useState();
const [translations, setTranslations] = useState([]);
const [isInEditMode, setIsInEditMode] = useState(false);
const [inputValue, setInputValue] = useState('samochod');
const [errors, setErrors] = useState([]);
const [translationsToSave, setTranslationsToSave] = useState([]);
const changeIsInEditMode = () => setIsInEditMode(!isInEditMode);
const handleEditButtonClick = (id) => console.log('Edit', id);
const handleDeleteButtonClick = (id) => console.log('Delete', id);
const handleInputChange = (e) => setInputValue(e.target.value);
const handleFetchOnButtonClick = async () => {
const resp = await fetch(`http://localhost:8080/pons/findTranslation/${inputValue}`).then(r => r.json()).catch(e => console.log(e));
if (resp.ok === true) {
setTranslations(resp.resp[0].hits);
setErrors([]);
} else {
setErrors(resp.errors ? resp.errors : ['Something went wrong. check the input']);
}
};
const handleSaveTranslations = async () => {
const resp = await fetch('localhost:8080/pons/', {method: 'POST', body: {content: translationsToSave}});
if (resp.ok === true) {
setInputValue('');
setTranslations(null);
}
};
return (
<div className="App">
{errors.length > 0 ? errors.map(e => <div key={e}>{e}</div>) : null}
<InputGroup className="mb-3">
<FormControl
value={inputValue}
onChange={handleInputChange}
placeholder={inputValue}
/>
</InputGroup>
<div className="mb-3">
<Button onClick={handleFetchOnButtonClick} disabled={inputValue === '' || errors.length > 0}>Translate</Button>
<Button onClick={changeIsInEditMode}>
{isInEditMode ? 'Exit edit mode' : 'Enter edit mode'}
</Button>
<Button disabled={translationsToSave.length === 0} onClick={handleSaveTranslations}>Save translations</Button>
</div>
<Table striped bordered hover>
<thead>
<tr>
<th>Original</th>
<th>Translation</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{translations ? translations.map(pon => pon.roms.map(rom => rom.arabs.map(arab => arab.translations.map(translation => {
const {source, target} = translation;
return (
<tr>
<td><span dangerouslySetInnerHTML={{__html: source}}/></td>
<td><span dangerouslySetInnerHTML={{__html: target}}/></td>
<td>
{
!translationsToSave.includes(target) ?
<Button onClick={() => {
const tmp = translationsToSave;
tmp.push(target);
setTranslationsToSave(tmp);
}}>
Add translation
</Button>
:
<Button
onClick={() => {
const tmp = translationsToSave;
tmp.splice(tmp.findIndex(elem => elem === target));
setTranslationsToSave(tmp);
}}>
Remove translation
</Button>
}
</td>
</tr>
)
})))) : (
<div>No translations</div>
)}
</tbody>
</Table>
</div>
);
}
export default App;
So it's a basic app, it right now just adds and removes from an array wit setTranslationsToSave. After I click the Add translation button the view stays the same. But it refreshes when I click Enter edit mode. Same with Remove translation. I need to click Enter/Exit edit mode.
Hitting Translate also reloads the view. So the Add/Remove translation buttons are the only ones which do not refresh the page. Why? What am I missing?
The issue is that you are mutating the satte in Add/Remove translation button, so when react check before re-rendering if the state updater was called with the same state it feels that nothing has changed as it does a reference check and ehnce doesn't trigger re-render
Also while updating current state based on previous state use functional callback approach for state updater.
Update your state like below
<Button onClick={() => {
setTranslationsToSave(prev => [...prev, target]);
}}>
Add translation
</Button>
:
<Button
onClick={() => {
setTranslationsToSave((prev) => {
const index = prev.findIndex(elem => elem === target)); return [...prev.slice(0, index), ...prev.slice(index + 1)]
});
}}>
Remove translation
</Button>
In your Add translation click handler, you're mutating the state:
<Button onClick={() => {
// tmp is just a reference to state
const tmp = translationsToSave;
// You are mutating state, this will be lost
tmp.push(target);
setTranslationsToSave(tmp);
}}>
You should duplicate the state and add the new element:
<Button onClick={() => {
setTranslationsToSave([...translationsToSave, target]);
}}>

Categories

Resources