I have two components, a parent component and its children components.
In the parent component I store a state for 'active' which holds the id of the active child component. What I'd like to do is have a handleClick function that compares the id of the child component which is being clicked to the value of 'active' and if it's the same, or different, i'd like it to update html className of the child component to achieve a certain style effect (which may include some animation).
Questions: Is there a way to target the className of a particular child element and update it?
Is it instead better to handle this function in the child itself while storing the id of the 'active' child in the state of the parent component?
If i'm looking to achieve css animations based on the change in className of the child component from one className to another, are there additional considerations, such as including that the animation run on render of the component, if i'm hoping to animate the change this way?
I'm sure there are other ways to approach this and i'm totally open to suggestions on the best approach to achieve the above, but I'll also include that I am just getting started with react and haven't learned about how to use hooks yet. I'm still working with basic functional and class based components.
Thanks in advance and example code with pseudo code below.
example parent component:
import React, {Component} from "react";
import Task from './openTasks';
import TasksData from './tasks-data';
class openTaskAccordion extends Component{
constructor(){
super()
this.state = {
//holds the id of the currently opened/active item but initialized to -1 since there is no item with an id of -1 at initialization.
active: -1
}
this.handleClick() = this.handleClick.bind(this);
}
handleClick(){
//if (the id of the clicked task === this.state.active){
// change the className of the clicked child component to "closed"
// } else {
// change the className of the child component with id == this.state.active to "closed", change the className of the clicked child component to "open" and update this.state.active to the id of the now open child component with setState.
//
}
render(){
const Tasks = TasksData.map(task=> <Task key={task.id} task ={task}/>)
return(
Tasks
)
}
}
export default openTaskAccordion
example child component
import React from "react";
import "./OpenTasks.css"
function openTasks(){
return (
<div id = {props.task.id} className="tasks" value = {props.task.id}>
<h1 >{props.task.clientName}</h1>
<div className="accordion-item accordion-item-closed" >
<h2>{props.task.matter}</h2>
<p> - {props.task.matterStatus}</p>
</div>
</div>
);
}
export default openTasks
Parent component
this
isn't bound correctly in constructor for handleClick
. Child component.
openTasks
is a functional component, so there is no this
, or rather, this
will just be undefined. openTasks
doesn't consume any of the props passed to it. Is there a way to target the className of a particular child element and update it?
You could do this, but direct DOM manipulations and reaching into other components to change things is anti-pattern in React. The react way is to pass data as props (data including what is or isn't "active") and letting children components handle it locally.
Is it instead better to handle this function in the child itself while storing the id of the 'active' child in the state of the parent component?
No, I don't think so, the parent should store the single source of truth about the current "active" child. Pass props to the child, including any callbacks the child could/should call to update state in the parent.
The parent component should store the active child, as you've done, but you should pass the active id and a callback to the children in order for them to be "clickable" and allow the parent to update what the active child is.
OpenTaskAccordion
this
binding in the constructorhandleClick
to consume a task id to toggle active state ofonClick
callback to Task
code
class OpenTaskAccordion extends Component {
constructor() {
super();
this.state = {
active: -1
};
this.handleClick = this.handleClick.bind(this); // <-- fix this binding
}
handleClick(id) { // <-- consume task id
this.setState((prevState) => ({
...prevState,
active: id === prevState.active ? -1 : id
}));
}
render() {
const { active } = this.state;
const { tasks = [] } = this.props;
return tasks.map((task) => (
<OpenTask
key={task.id}
active={active}
task={task}
onClick={this.handleClick}
/>
));
}
}
OpenTasks
this
references since this is a functional component.props.active
matches the current task id.props.onClick
to something clickable to toggle the active state in parent.code
function OpenTask(props) { // <-- consume `props`!!
return (
<div
id={props.task.id}
className={["tasks", props.active === props.task.id && "active"].join(" ")}
value={props.task.id}
onClick={() => props.onClick(props.task.id)} // <-- attach onClick callback
>
<h1>{props.task.clientName}</h1>
<div className="accordion-item accordion-item-closed">
<h2>{props.task.matter}</h2>
<p> - {props.task.matterStatus}</p>
</div>
</div>
);
}
CSS used to apply an "animation" for active item toggling. Uses a simple CSS transition on the background color.
.tasks {
transition: background-color ease-in-out 0.5s;
}
.active {
background-color: lightblue;
}
In the functional child component, where can I read about what this is doing?
className={["tasks", props.active === props.task.id && "active"].join(" ")}
It is simply a way to create a list of space-separated class names, ie "tasks" or "tasks active" from ["tasks"]
or ["tasks", "active"]
.
Some alternative methods include
className={`tasks ${props.active === active ? "active" : ""}`}
className={"tasks" + `${props.active === active ? " active" : ""}`}
In the parent class component, what is this doing and why isn't
{tasks =[] }
overridden by the data set you created in your example?const { active } = this.state; const { tasks = [] } = this.props;
const { tasks = [] } = this.props;
is just a way to provide a defined value for the mapping in the case that this.props.tasks
is undefined (or falsey), as would be the case if a tasks
prop was not passed to the component. So long as this.prop.tasks
is a defined truth value then that is what is used. Consider this a fallback value.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.