So I've got two input boxes that I want to be able to toggle (hide) between by clicking on them. They populate a table below them. I know it's not difficult but can't seem to make it happen easily.
It's all happening in the one component. Something like this:
<p id="toggle">
<span> Employer </span>
<span> Location </span>
</p>
<div id="left">..input box 1</div>
<div id="right">..input box 2</div>
What's the function I'd need to implement it? Thanks!
Your issue is indeed not very difficult.
Here is a solution with a function component:
const MyComponent = (props) => {
const [selected, setSelected] = useState(0)
return (
<div>
<p id="toggle">
<span onClick={() => setSelected(0)}> Employer </span>
<span onClick={() => setSelected(1)}> Location </span>
</p>
{(selected === 0) && <div id="left"> ..input box 1</div>}
{(selected === 1) && <div id="right"> ..input box 2</div>}
</div>
)
}
Here is a solution with a class component:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
selected: 0
}
}
render() {
return (
<div>
<p id="toggle">
<span onClick={() => this.setState({ selected: 0 })}> Employer </span>
<span onClick={() => this.setState({ selected: 1 })}> Location </span>
</p>
{(selected === 0) && <div id="left"> ..input box 1</div>}
{(selected === 1) && <div id="right"> ..input box 2</div>}
</div>
)
}
}
If I'm understanding you're wanting to toggle the display of the div elemements on clicking the span elements (why not use anchors they're better suited). Below is a simple way to implement this,
(() =>
{
const togglers = document.querySelectorAll('#toggle span');
const togglees = document.querySelector('#toggle').parentNode.querySelectorAll('div');
togglers.forEach((toggler, index) =>
{
toggler.addEventListener('click', () =>
{
togglees.forEach(togglee => togglee.style.display = 'none');
togglees[index].style.display = 'block';
}, true);
});
})();
/* HIDE THOSE TOGGLEES BY DEFAULT CHANGE THIS!!!!!!!! */
div > div
{
display: none;
}
<div>
<p id="toggle">
<span> Employer </span>
<span> Location </span>
</p>
<div id="left"> ..input box 1</div>
<div id="right"> ..input box 2</div>
</div>
Related
I am writing a simple blog component in React. The component get the post from a second file and renders the content and the hashtags with map (in particular two nested map). I was trying to create a part in which the hashtag are highlighted when clicked, and hence I was following the snippet of the answer to this question. The fact is that it is not working and in particular what it is not working is the binding through bind (checked with console.log output)
class Blog extends React.Component {
state= {
open: {}
}
handleClick = (k) => {
let linkOpenState = true;
if (this.state.open.hasOwnProperty(k)) {
linkOpenState = !this.state.open[k];
}
this.setState({ open: { [k]: linkOpenState } })
}
render(){
return(
posts.map(i=>(
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span id={k} onClick={this.handleClick.bind(this,k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
)
}
}
export default Blog
Anyone has an idea of what is wrong there? Is it the nested map a problem for the bind?
React expects a single element as a return. You can solve this by wrapping the return in a fragment as <React.Fragment> or <>.
Also, you don't need to bind an arrow function(handleClick when mapping i.hash).
render(){
return(
<>
posts.map(i=>(
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span key={k} id={k} onClick={() => this.handleClick(k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
</>)}}
You don't need to bind the arrow function.
Also, you need to pass unique key to elements when used inside map, you can use index as a key but if you are mutating the array then use some id or hash or anything which will be unique.
class Blog extends React.Component {
state= {
open: {}
}
handleClick = (k) => {
let linkOpenState = false;
if (this.state.open.hasOwnProperty(k)) {
linkOpenState = this.state.open[k];
}
this.setState({ open: { [k]: linkOpenState } })
}
render(){
return(
posts.map((i, index)=>(
<div class="box" key={i.id || index}>
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span key={k} id={k} onClick={() => this.handleClick(k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
)
}
}
export default Blog
I am creating a Next js site mcqs and answer.
Initially only questions are rendered with the title and options.
There is a button to show the answer of given question but when button is clicked it is changing the state of all elements in the map function instead of element that is clicked.
export default function Chapter({ chapter }) {
const [right, setRight] = useState(false)
function handleOnClick() {
setRight(!right)
}
<main className='main_q'>
<h1>{chapter.items[0].name}</h1>
{chapter.items[0].questions.items.map((question, index) => {
return (
<div key={question.id} className='question_container'>
<div className='question_q_t'>
<div className='question_q'>Q</div>
<div className='question_t'>
<Markdown>{question.title}</Markdown>
</div>
</div>
<div className='option_container'>
<div className='option_a_o'>
<div className='option_a'>A</div>
<div className='option_o'>
<Markdown>{question.optionA}</Markdown>
</div>
</div>
<div className='option_a_o'>
<div className='option_a'>B</div>
<div className='option_o'>
<Markdown>{question.optionB}</Markdown>
</div>
</div>
<div className='option_a_o'>
<div className='option_a'>C</div>
<div className='option_o'>
<Markdown>{question.optionC}</Markdown>
</div>
</div>
<div className='option_a_o'>
<div className='option_a'>D</div>
<div className='option_o'>
<Markdown>{question.optionD}</Markdown>
</div>
</div>
</div>
<a
className='solution_link'
target='_blank'
href={`/${question.chapter.subject.category.slug}/${question.chapter.subject.slug}/${question.chapter.slug}/${question.id}`}
>
See Solution
</a>
<button onClick={handleOnClick}>Answer</button>
{right && <div>{question.rightAnswer}</div>}
</div>
)
})}
</main>
}
Put each question div into its own component, and make a right state in that component:
const Question = ({ question }) => {
const [right, setRight] = useState(false)
return (
<div key={question.id} className='question_container'>
// etc
Or make an array of right states in the parent:
export default function Chapter({ chapter }) {
const qs = chapter.items[0].questions.items;
const [rights, setRights] = useState(qs.map(q => false));
const makeHandleClick = (i) => {
setRights(
rights.map((r, j) => j === i ? r : !r)
);
};
// ...
<button onClick={makeHandleClick(i)}>Answer</button>
{rights[i] && <div>{question.rightAnswer}</div>}
this is my code i'm trying to add a class on click to a clicked div but i have more than 1 div have the same classname so when i click to any
div the toggle class added to all divs not the one which i clicked what i want to know how can i specify the div which i want to add toggle class to it
Code:
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive)
console.log('sad')
}
return (
<div className="command_wrapper">
<div className="command_list">
{
Commands.map((item, index) => {
return (
<div className="command" >
<div className="command_face" onClick={toggleClass}>
<h1>{item.Command}</h1>
<p>{item.Description}</p>
</div>
<div className={isActive ? "command_body" : "disapper"}>
<div className="command_usage">
<h1>Usage</h1>
<p>{item.Usage}</p>
</div>
<div className="command_required">
<h1>Required Permissions</h1>
<p>{item.premissions}</p>
</div>
</div>
</div>
)
})
}
</div>
</div>
```
You can pass the item inside toggle function and verify if item passed is activated or just save the item clicked.
const [itemActive, setItemActive] = useState();
const toggleClass = (item) => {
setItemActive(item)
}
return (
<div className="command_wrapper">
<div className="command_list">
{
Commands.map((item, index) => {
return (
<div className="command" >
<div className="command_face" onClick={() => toggleClass(item)}>
<h1>{item.Command}</h1>
<p>{item.Description}</p>
</div>
<div className={item === itemActive ? "command_body" : "disapper"}>
<div className="command_usage">
<h1>Usage</h1>
<p>{item.Usage}</p>
</div>
<div className="command_required">
<h1>Required Permissions</h1>
<p>{item.premissions}</p>
</div>
</div>
</div>
)
})
}
</div>
</div>
```
If item has any unique property, you can save just then, like an ID or name. I think it will be better.
/*const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive)
console.log('sad')
}*/
function handleClick(el){
//toggle the class which indicates selection ex. 'active'
el.classList.toggle('active');
//other stuff todo
}
return (
<div className="command_wrapper">
<div className="command_list">
{
Commands.map((item, index) => {
return (
<div className="command" >
//destruct event objet{event.target}
<div onClick={({target})=>handleClick(target)}>
<h1>{item.Command}</h1>
<p>{item.Description}</p>
</div>
....
...
</div>
)
})
}
</div>
</div>
I want add active class to click element but it is toogle. How can i fixed it?
const PodcastTab = () => {
const [tabOpened, setTabOpened] = useState(false);
return <div>
<div>
<span className={classNames('my-podcast',{'active':tabOpened})} onClick={()=>{setTabOpened(!tabOpened)}}>My Podcast</span>
<span className={classNames('podcasts-stats',{'active':!tabOpened})} onClick={()=>{setTabOpened(!tabOpened)}}>Podcast's Stats</span>
<div className="my-podcast-tab">
<Podcast />
</div>
<div className="podcasts-stats-tab">
<Podcast imgURL="https://i.picsum.photos/id/144/75/75.jpg?hmac=9kweqWXv0sL19dFj1CaMKxbH3kQMIuFFbHy2hWhKJ4w" status="x" title="y"/>
</div>
</div>
</div>;
}
You can use the following className:
className={classNames('podcasts-stats',{tabOpened ? 'active' : ''})}
Newbie dev learning React.
I'm trying to create an upvote functionality to a blog post in React but when I click on the upvote button I'm upvoting all of the blog post cards at once instead of the individual card.
How can I fix this? I believe the issue may be in the way I'm setting setState? But I may be wrong and looking for help.
Thanks in advance!
====
class Posts extends Component {
state= {
points: 0
}
componentDidMount() {
this.props.fetchPosts()
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.posts.unshift(nextProps.newPost);
}
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render() {
const postItems = this.props.posts.map((post, index) => (
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{post.title}</div>
<div className="meta"> {post.author}</div>
<div className="description">
<p>{post.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
))
return (
<div>
<br />
<h2 className="ui header">
<i className="pencil alternate icon"></i>
<div className="content">
Blog Feed
<div className="sub header">Create New Post!</div>
</div>
</h2>
{postItems}
</div>
)
}
}
You have a single component storing the "points" state for all your posts. To achieve the functionality you described, each post should be it's own component with it's own state.
class Post extends Component {
state = {
points: 0
}
handleClick = () => {
this.setState({points: this.state.points + 1})
}
render = () =>
<div key={index} className="ui three stackable cards">
<div className="ui card">
<div className="content">
<div className="header">{this.props.title}</div>
<div className="meta"> {this.props.author}</div>
<div className="description">
<p>{this.props.body}</p>
</div>
</div>
<div className="extra content">
<i className="check icon"></i>
{this.state.points} Votes
</div>
<button className="ui button"
type="submit"
onClick={this.handleClick}>Add Point</button>
</div>
</div>
}
}
You are upvoting every card because you have only one counter. A separate counter should be defined for every card.
state = {}; // dictionary-a-like state structure
handleClick = (id) => () => {
this.setState((prevState) => ({
[id]: prevState[id] ? prevState[id] + 1 : 1, // check and increment counter
}));
}
onClick={this.handleClick(post.id)} // call function with post.id as argument
{this.state[post.id] || 0} Votes // display votes for every card
Note: I assumed that every card has it's own unique id, if not - index may come handy too.
You will need one counter for each post. Currently you only have a single counter for all posts, which means that they all display that same value.
The best way to achieve this would probably be to separate your post into its own component, and have that keep track of the counter.
The following solution uses a post ID (if you have it) to create a key in a stateful points object. Then, on click, you can add to the correct points key.
state = {
points: {}
}
handleClick = postId => {
this.setState({
points: {
...this.state.points,
[postId]: (this.state.points[postId] || 0) + 1
}
})
}
const postItems = this.props.posts.map((post, index) => (
...
<div className="extra content">
<i className="check icon"></i>
{this.state.points[post.id] || 0} Votes
</div>
<button
className="ui button"
type="submit"
onClick={() => this.handleClick(post.id)}
>
Add Point
</button>
...
)