I need to make class active on when box is clicked using React hooks.
The box is not a simple div element, but is dynamically loaded from an array.
I have problem with this funtion:
const changeClassName = (props) => {
return props.className.replace("box active");
};
This doesn't change the className property.
Codepen link
Full code:
import React, { useState } from "https://cdn.skypack.dev/react#17.0.1";
import ReactDOM from "https://cdn.skypack.dev/react-dom#17.0.1"
const sounds = [
{
key: "A",
mp3: "https://audiodeploy.netlify.app/8%20El-Mu'minun,%20115-116.mp3",
},
{
key: "B",
mp3: "https://audiodeploy.netlify.app/8%20El-Mu'minun,%20115-116.mp3",
}
];
const App = () => {
const [keys, setKeys] = useState([
"1","2"
]);
const [active, setActive]=useState(false);
return (
<>
<span id="text"></span>
<div id="display" className="display">
{sounds.map((sound, id) => {
return <Box text={sound.key} audio={sound.mp3} />;
})}
</div>
</>
);
};
const changeClassName=(props)=>{
return(
props.className.replace("box active") // this doesn't change the className
)
}
const playSound = (audioRef,props) => {
audioRef.current.play();
console.log(props.text);
if(props.text==='A'||props.text=='B')
ReactDOM.render(
<>
<p> Then did you think that We created you uselessly and that to Us you would not be returned?"</p>
<p>So exalted is Allāh, the Sovereign, the Truth; there is no deity except Him, Lord of the Noble Throne.</p>
<p>[Quran, 23:115-116]</p>
</>
, document.getElementById('text'));
changeClassName(props);
};
const Box = (props) => {
const audioRef = React.useRef();
return (
<div className="box" onClick={() => playSound(audioRef,props)}>
{props.text}
<audio
src={props.audio}
ref={audioRef}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Could you please help me to fix this?
This should do the work for the active part, but you may need to clean that up a little bit
https://codepen.io/quentinbenyahia/pen/yLqaEvv
const App = () => {
const [isActive, setIsActive] = useState(false);
const playSound = (audioRef,id) => {
audioRef.current.play();
ReactDOM.render(
<>
<p> Then did you think that We created you uselessly and that to Us you would not be returned?"</p>
<p>So exalted is Allāh, the Sovereign, the Truth; there is no deity except Him, Lord of the Noble Throne.</p>
<p>[Quran, 23:115-116]</p>
</>
, document.getElementById('text'));
setIsActive(id)
};
return (
<>
<span id="text"></span>
<div id="display" className="display">
{sounds.map((sound, id) => {
return <Box playSound={(audioRef) => { playSound(audioRef, id ) }} isActive={id === isActive} text={sound.key} audio={sound.mp3} />;
})}
</div>
</>
);
};
const Box = (props) => {
const audioRef = React.useRef();
return (
<div className={props.isActive ? "box active": "box"} onClick={() => props.playSound(audioRef)}>
{props.text}
<audio
src={props.audio}
ref={audioRef}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
you can follow these steps:
1- const [isActive , setIsActive] = useState(false)
2- const changeClassName=(props)=>{
setIsActive(true)
}
3- <div className={isActive ? "box active": "box"} onClick={() => playSound(audioRef,props)}>
{props.text}
<audio
src={props.audio}
ref={audioRef}
/>
</div>
Related
I have a very basic react component like this
const Message = (props) => {
const [show, setShow] = useState(false);
return (
<p show={show}>My Message</p>
);
};
I want to use this component from another one, and I want to be able to show the first one by clicking on a button in the second one
const OtherComponent = (props) => {
return (
<>
<Message />
<Button onClick={setShow(true)}>Open Message</Button>
</>
);
};
of course this code does not work, is there a way to achieve this or is Redux my only option?
Move state to parent
const Message = ({ show }) => {
return (
<p show={show}>My Message</p>
);
};
const OtherComponent = (props) => {
const [show, setShow] = useState(false);
return (
<>
<Message show={show} />
<Button onClick={setShow(true)}>Open Message</Button>
</>
);
};
I want to add to Chip an startIcon={<Icon />}
when click on a Chip.
The state of the icon is managed by chipsState.
In this code,
the state of all chips would change.
How can I change only the chipsState of the element that is clicked?
In this code, the state of all chips will change.
How can I change only the chipsState of the element that is clicked?
const Modal:React.FC<Props>= (props) => {
const {modalData} = props;
const [chipsState, setChipsState] = useState(false);
const onChipClick = (element:any) => {
setChipsState(chipsState => !chipsState);
}
return (
<div>
{
modalData.symtoms.map((element:any, index:number) => (
<div key={index}>
<Chip onClick={() => onChipClick(element)} startIcon={chipsState && <Icon />}>{element.description}</Chip>
</div>
))}
</div>
);
}
export default Modal;
To handle local state (and better testing), you should create a new custom Chip component with dedicated chipState.
interface CustomChipProps {
description: string
}
const CustomChip = (props: CustomChipProps) => {
const [chipState, setChipState] = useState(false);
return <Chip onClick={() => setChipState(prev => !prev)} startIcon={chipState && <Icon />}>{props.description}</Chip>;
}
const Modal:React.FC<Props>= (props) => {
const {modalData} = props;
return (
<div>
{
modalData.symtoms.map((element:any, index:number) => (
<div key={index}>
<CustomChip description={element.description} />
</div>
))}
</div>
);
}
export default Modal;
You can achieve your desired output by changing chipState state from boolean to object.
So first let's change to object state instead of boolean
const [chipsState, setChipsState] = useState({});
Now we will change onChipClick function to change value of selected chip state
const onChipClick = (element:any) => {
setChipsState({...chipsState, chipsState[element]: !chipsState[element]});
}
And finally we will read correct value of each chipsState element.
<Chip onClick={() => onChipClick(element)} startIcon={chipsState[element] && <Icon />}>{element.description}</Chip>
You can try like the following
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
import { Grid, Row } from "react-flexbox-grid";
const ChipSet = ({ symtomsData }) => {
const data = symtomsData.map((symtom) => ({ ...symtom, isSelcted: false }));
const [chipSets, setChipSets] = useState(data);
const onSelectChipSet = useCallback(
(e, index) => {
const updatedChipSets = chipSets.map((chip, i) =>
i === index ? { ...chip, isSelcted: e.target.checked } : chip
);
setChipSets(updatedChipSets);
},
[chipSets]
);
console.log("chipSets", chipSets);
return (
<div>
<h1>Symtoms Data</h1>
{chipSets.map((x, i) => (
<div key={i}>
<label>
<input
onChange={(e) => onSelectChipSet(e, i)}
type="checkbox"
value={x.isSelcted}
/>
{x.description}
</label>
</div>
))}
</div>
);
};
class App extends React.Component {
render() {
const symtomsData = [
{
description: "mild"
},
{
description: "cold"
}
];
return (
<Grid>
<Row>
<ChipSet symtomsData={symtomsData} />
</Row>
</Grid>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I am having a hard time rendering components conditionally in React. I have successfully rendered 2 components (A and B) conditionally but couldn't find any successful way to add a third component (C) in our case
this is the code for 2 componnets:
function App() {
const [click, setClick] = useState(true);
const ShowA = () => setClick(true);
const ShowB = () => setClick(false);
return (
<>
<br />
<button onClick={ShowA}>A </button>
<button onClick={ShowB}>B </button>
<div className="App">
{click && <div> A </div>}
{!click && <div>B</div>}
</div>
</>
);
}
Is there any possible way I can add a third C component so I can toggle between them? I have been trying for 2 days but no success.
This is the link of Codesandbox if anyone's interested
https://codesandbox.io/s/musing-tesla-9gkpw?file=/src/index.js:100-481
You can put as many states as you want:
function App() {
const [displayA, setDisplayA] = useState(true);
const [displayB, setDisplayB] = useState(true);
const [displayC, setDisplayC] = useState(true);
const showA = () => {
setDisplayA(true);
setDisplayB(false);
setDisplayC(false);
}
const showB = () => {
setDisplayA(false);
setDisplayB(true);
setDisplayC(false);
};
const showC = () => {
setDisplayA(false);
setDisplayB(false);
setDisplayC(true);
};
return (
<>
<br />
<button onClick={showA}>A</button>
<button onClick={showB}>B</button>
<button onClick={showC}>C</button>
<div className="App">
{displayA && <div>A</div>}
{displayB && <div>B</div>}
{displayC && <div>C</div>}
</div>
</>
);
}
And you can even put other things in your state, like JSX elements:
function App() {
const [elementToDisplay, setElementToDisplay] = useState("");
const showA = () => {
setElementToDisplay(<div>A</div>)
}
const showB = () => {
setElementToDisplay(<div>B</div>)
}
const showC = () => {
setElementToDisplay(<div>C</div>)
}
return (
<>
<br />
<button onClick={showA}>A</button>
<button onClick={showB}>B</button>
<button onClick={showC}>C</button>
<div className="App">
{elementToDisplay}
</div>
</>
);
}
You can save a state for the current button, and then show the different button conditionally using object lookup:
Check https://codesandbox.io/s/kind-haslett-b0fv0
function App() {
const [currentButton, setCurrentButton] = useState('A');
return (
<>
<br />
<button onClick={() => setCurrentButton('A')}>A</button>
<button onClick={() => setCurrentButton('B')}>B</button>
<button onClick={() => setCurrentButton('C')}>C</button>
<div className="App">
{
({
A: <div>A</div>,
B: <div>B</div>,
C: <div>C</div>
})[currentButton]
}
</div>
</>
);
}
I have index.js file where I have rendered the App component .
Index.js file
ReactDOM.render(<App />, document.getElementById('root'));
Below is the code for SettingContainer.js file where I have SettingContainer component. I have a button on click of it I need to rerender <SettingContainer value="10" /> But It doesn't render with defaultvalues.
SettingContainer.js file:
import React from 'react';
const SettingContainer = (props) => {
const [state, setState] = React.useState({
currentValue: props.value
});
const handleChange = (event) => {
setState({ currentValue: event.target.value });
};
return (
<React.Fragment>
<input type='text' value={state.currentValue} onChange={handleChange} />
</React.Fragment>
)
};
export default SettingContainer;
Below is the code for the App.js file where I have App component.
App.js file
const handleClick = () => {
ReactDOM.render(<SettingContainer value="10" />, document.getElementById('divHello'));
};
const App = () => {
return (
<>
<div id="divHello">
<SettingContainer value="10" />
</div>
<button onClick={handleClick}>Button</button>
</>
);
};
export default App;
Actually, your issue comes back to your mindset, you should change your thoughts about ReactJS. you should have an Index container like below:
const Index = () => {
const [isRender, renderSettingContainer] = useState(false);
return (
<>
{isRender && (
<SettingContainer />
)}
<App onClick={renderSettingContainer}>
</>;
);
};
Then, pass the onClick function from props to the App like below:
const App = ({ onClick }) => (
<>
Hello Friends
<div id="divHello">
</div>
<button onClick={onClick}>Button</button>
</>
);
Also, there is no need to use ReactDOM twice, so write it like below:
ReactDOM.render(<Index />, document.getElementById('root'));
If you have any questions, write a comment, definitely, I will answer and will change my answer.
Hint: the <></> is just like <React.Fragment></React.Fragment> with less code and better performance, based on Dan Abramov idea.
Use conditional rendering, on press button set value to display Hello component.
const Hello = () => (<p>Hello</p>)
Then in App set value to true on button press.
const App = () => {
const [displayHello, setDisplayHello] = useState(false);
const handleClick = () => {
setDisplayHello(!displayHello)
};
return (
<React.Fragment>
Hello Friends
<div id="divHello">
</div>
{displayHello && <Hello />}
<button onClick={handleClick}>Button</button>
</React.Fragment>
);
};
// Get a hook function
const {useState} = React;
const Hello = () => (<p style={{ backgroundColor: 'green', color: 'white'}}>Hi from Hello Component</p>)
const App = () => {
const [displayHello, setDisplayHello] = useState(false);
const handleClick = () => {
setDisplayHello(!displayHello)
};
return (
<React.Fragment>
Hello Friends
<div id="divHello">
</div>
{displayHello && <Hello />}
<button onClick={handleClick}>Button</button>
</React.Fragment>
);
};
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I've added a 'like' function to increase the like count on a photo in any given iteration.
The issue is all of the photos in the gallery get liked at the same time.
I want to like photos individually.
const Gallery = ({ initialData, initialDataSetTwo, initialDataSetThree }) => {
const [data, setData] = useState(initialData);
const [dataTwo, setDataTwo] = useState(initialDataSetTwo);
const [dataThree, setDataThree] = useState(initialDataSetThree);
const [likes, setLikes] = useState(0);
const addLike = () => {
setLikes(likes + 1);
};
return (
<Wrapper>
<Styles className="row">
<Div className="col-xs-4">
{data.map(item => (
<MyImage
key={item.id}
src={item.fields.image.file.url}
header={item.fields.name}
likes={likes}
addLike={addLike}
/>
))}
</Div>
Here, within the icon, I have an onClick function that update the likes.
Again, I'm able to increment likes, but all the photos get liked at once.
How should I do this? Thanks!
const MyImage = ({ src, header, likes, addLike }) => {
const [ref, hovered] = useHover();
return (
<MyImageDiv ref={ref} className="row imageSpace">
{hovered && (
<div className="name">
<h1>
{header} <span className="likespan">{likes}</span>{" "}
<i onClick={() => addLike()} class="likeicon far fa-heart"></i>
</h1>{" "}
</div>
)}
<img className="image" alt="fall" src={src} />
</MyImageDiv>
);
};
You have your likes state set up in your gallery component. Even though you're mapping across each image you still assign the likes prop likes={likes} to the same state.
You'll want to move your likes state (and addLikes function) into your "MyImage" component, so each MyImage has its own separate bit of state.
You need to maintain a like variable in the data model itself since the likes property needs to be present for each item and not one as a whole
const Gallery = ({ initialData, initialDataSetTwo, initialDataSetThree }) => {
const [data, setData] = useState(initialData);
const [dataTwo, setDataTwo] = useState(initialDataSetTwo);
const [dataThree, setDataThree] = useState(initialDataSetThree);
const addLike = (index) => {
setData(prev => [...prev.slice(0, index), {...prev[index], likes: prev[index].likes + 1}, ...prev.slice(index+1)])
};
return (
<Wrapper>
<Styles className="row">
<Div className="col-xs-4">
{data.map((item, index) => (
<MyImage
key={item.id}
index={index}
src={item.fields.image.file.url}
header={item.fields.name}
likes={likes}
addLike={addLike}
/>
))}
</Div>
</Styles>
</Wrapper>
)
}
const MyImage = ({ src, index, header, likes, addLike }) => {
const [ref, hovered] = useHover();
return (
<MyImageDiv ref={ref} className="row imageSpace">
{hovered && (
<div className="name">
<h1>
{header} <span className="likespan">{likes}</span>{" "}
<i onClick={() => addLike(index)} class="likeicon far fa-heart"></i>
</h1>{" "}
</div>
)}
<img className="image" alt="fall" src={src} />
</MyImageDiv>
);
};