I have this code:
class ItemWrap extends Component {
constructor() {
super();
this.state = { showItem: true};
this.removeItem = this.removeItem.bind(this);
}
removeItem() {
this.setState({ showItem: false });
}
render() {
var item = this.state.showItem ? <Item data_items={this.props.data_items} /> : '';
return (
<div id="sss">
{item}
<button onClick={this.removeItem}>remove image</button>
</div>
);
}
}
export default ItemWrap;
On button click I remove {item}. But the button is stay. I need to remove all ItemWrap after button click.
Help me )
Firstly,removeItem function is designed for change flag of state,
and then you can use this flag to veiw whatever you want.
ex:
if(flag)
return (your current div);
else
return(
whatever you want , empty or other
)
The rendering should be managed in the parent component, here is a possible solution
class ItemWrap extends Component {
constructor() {
super();
}
render() {
return (
<div id="sss">
<Item data_items={this.props.data_items} />
<button onClick={this.props.onClickBtn}>remove image</button>
</div>
);
}
}
export default ItemWrap;
then in the wrapper you can manage the rendering of ItemWrap
class Wrapper extends Component {
constructor() {
super();
this.state = { showItem: true};
this.removeItem= this.removeItem.bind(this);
}
removeItem() {
this.setState({ showItem: false });
}
render() {
return (
<div>
{ this.state.showItem && <ItemWrapper onClickBtn={this.removeItem} /> }
</div>
);
}
}
export default Wrapper;
Related
So I am quite new to React world, and I have this problem I am trying to solve, but I don't quite understand why it is happening.
So I want to pass the state of component to parent component and from parent component to child component and everything look okay, and in console log the state goes trough, but nothing changes. I believe there is a way I need to listen for state change or something within child component so it works. If I put true in the parent component, child component also get's true, but if I toggle it on click, it goes trough but nothing changes in the child component.
Also I understand my code is little rough right now ill reafactor it later, but right now I am trying to understand why it does not work.
If anyone could help me I would be thankful for it.
This is component that controls the state.. So the state passes from TurnOnBtn to App and from App it goes to TodoList
import "./Todo.css";
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.state = { display: false };
this.handleState = this.handleState.bind(this);
}
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
export default TurnOnBtn;
parent component App
import TurnOnBtn from "./TurnOnBtn";
import TheMatrix from "./TheMatrxHasYou";
import TodoList from "./TodoList";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.checkDisplay = this.checkDisplay.bind(this);
}
checkDisplay(newDisplay) {
this.setState({
display: newDisplay,
});
console.log(this.state);
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn checkDisplay={this.checkDisplay} />
</div>
);
}
}
export default App;
child component TodoList
import Todo from "./Todo";
import NewTodoForm from "./NewTodoForm";
import { v4 as uuid } from "uuid";
import "./Todo.css";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
todos: [],
displayOn: this.props.display,
};
this.newTodo = this.newTodo.bind(this);
this.editTodo = this.editTodo.bind(this);
this.deleteTodo = this.deleteTodo.bind(this);
}
editTodo(id, updatedTask) {
const updatedTodo = this.state.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, todo: updatedTask };
}
return todo;
});
this.setState({
todos: updatedTodo,
});
console.log(updatedTask);
}
deleteTodo(id) {
this.setState({
todos: this.state.todos.filter((todo) => todo.id !== id),
});
}
newTodo(newState) {
this.setState({
todos: [...this.state.todos, { ...newState }],
});
}
render() {
return (
<div
style={this.state.displayOn ? { opacity: 1 } : { opacity: 0 }}
className="Todo-screen"
>
{" "}
<div className="TodoList">
<div className="TodoList-todos">
{" "}
{this.state.todos.map((todo) => (
<Todo
key={uuid()}
id={todo.id}
active={todo.active}
editTodo={this.editTodo}
deleteTodo={this.deleteTodo}
todoItem={todo.todo}
/>
))}
</div>
</div>{" "}
<NewTodoForm newTodo={this.newTodo} />
</div>
);
}
}
export default TodoList;
The bug here is in these line of codes:
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
Remember setState is an async function, so by the time you set a new state using setState, the value for this.state is not guaranteed changed.
One way to fix this is using the setState callback, which will run after the state is changed:
handleState() {
this.setState({ display: !this.state.display }, function() {
this.props.checkDisplay(this.state.display);
});
}
But you don't need to use another state to keep display state in TurnOnBtn as you can pass the toggle callback from the parent:
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState({
display: !this.state.display,
});
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn toggleDisplay={this.toggleDisplay} />
</div>
);
}
}
TurnOnBtn.js
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.handleState = this.handleState.bind(this);
}
handleState() {
this.props.toggleDisplay();
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
So I have a component "itemSelection" and inside of it I map through an api response like this
<div className="row">
{this.state.items.map(i => <Item name={i.name} quantity={i.quantity} />)}
</div>
Here the state of "Item" component
constructor(props){
super(props);
this.state = {
visible: false,
selected: false,
}
}
How could I pass the state of "Item" component to "itemSelection" component?
Sending data back up to your parent component should be done by using props.
Fairly common question, see this post for the long answer.
As according to me, If I understood your question you want to call the state of the child component to the parent component.
//Child.js
import s from './Child.css';
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
export default withStyles(s)(Child);
//Parent.js
class Parent extends Component {
render() {
onClick() {
this.refs.child.getAlert()
}
return (
<div>
<Child ref="child" />
<button onClick={this.onClick.bind(this)}>Click</button>
</div>
);
}
}
Also, you can get the code reference from the link: https://github.com/kriasoft/react-starter-kit/issues/909
This a little tricky but Maybe, its help you solving your problem.
//Parent.js
class Parent extends Component {
component(props) {
super(props)
this.state = {
test: 'abc'
}
}
ParentFunction = (value) => {
this.state.test = value;
this.setState(this.state);
}
render() {
return (
<div>
<Child
test={this.state.test}
ParentFunction={this.ParentFunction}
/>
</div>
);
}
}
//Child.js
import s from './Child.css';
class Child extends Component {
component(props) {
super(props)
this.state = {
test: props.test
}
}
handleChange = () => {
this.state.test = event.target.value;
this.setState(this.state);
this.handleOnSave()
}
handleOnSave = () => {
this.props.ParentFunction(this.state.test);
}
render() {
return (
<div>
<input type="text" onChange={this.handleChange} />
</div>
);
}
}
export default withStyles(s)(Child);
The following code obviously doesn't work
But the Idea is, I would like to conditional rendering depending on a child component's clientHeight, not sure if it's possible
class App extends Component {
constructor() {
super();
this.comp = {};
this.state = {
clientHeight: 0
};
}
componentDidMount() {
console.log(this.comp)
this.setState({
clientHeight: this.comp.clientHeight
});
}
render() {
return (
<div>
<Hello name={this.state.name} ref={comp => this.comp = comp}/>
{
this.state.clientHeight > 100 ? <ComponentA /> : <ComponentB />
}
</div>
);
}
}
you could do this by setting clientHeight value from <Hello/> component
class App extends Component {
constructor() {
super();
this.state = {
clientHeight: 0
};
}
handler(newSize) {
this.setState({
clientHeight: newSize
})
}
render() {
return (
<div>
<Hello name={this.state.name} onResize={this.handler}/>
{
this.state.clientHeight > 100 ? <ComponentA /> : <ComponentB />
}
</div>
);
}
}
you can get window height by window.addEventListener('resize', ((c)=>{console.log(window.innerHeight)}));
It turn out #Dane's idea helped me. with componentDidUpdate hook. this achiever what I want, but it renders twice every time, one for the ref to be assigned, and change update to the new clientHeight, then render again base on the new clientHeight. Not sure if there is a better way.
class App extends Component {
constructor() {
super();
this.comp = {};
this.state = {
clientHeight: 0
};
}
componentDidUpdate(prevProps){
if(prevProps !== this.props){
this.setState({
clientHeight: this.comp.clientHeight
})
}
}
render() {
return (
<div>
<Hello name={this.state.name} ref={comp => this.comp = comp}/>
{
this.state.clientHeight > 100 ? <ComponentA /> : <ComponentB />
}
</div>
);
}
}
I created a custom Accordion component which again consist of two child components called AccordionTitle and AccordionContent:
The AccordionTitle component has a button. When clicked, the AccordionContent part toggles its style from display:none to block and back when clicked again.
AccordionTitle.js
class AccordionTitle extends Component {
constructor() {
super();
this.show = false;
}
toggle() {
this.show = !this.show;
if (this.props.onToggled) this.props.onToggled(this.show);
}
render() {
return (
<div style={this.props.style}>
<Button onClick={e => this.toggle(e)} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
AccordionContent.js
class AccordionContent extends Component {
render() {
let style = this.props.style ? this.props.style : {};
style = JSON.parse(JSON.stringify(style));
style.display = this.props.show ? 'block' : 'none';
return (
<div style={style}>
{this.props.children}
</div>
);
}
}
export default AccordionContent;
Also, I use the following parent component:
Accordion.js
class Accordion extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
Accordion.Title = AccordionTitle;
Accordion.Content = AccordionContent;
export default Accordion;
Now, when I use the Accordion component, it's possible that I might need multiple accordions in a row which would look like this:
ProductAccordion.js
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: false,
};
}
toggled() {
this.setState({
show: !this.state.show,
});
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, this)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
As you can see, I am grabbing the toggled Event from Accordion.Title and I bind it to the prop show of Accordion.Content via the toggled() method.
Now, this works perfectly fine as long as there is just one product, but if there are more of them, clicking on the button will toggle all AccordionContent instances.
How can I change this so that only the content-part which belongs to the title that contains the clicked button will be toggled?
I also have the feeling that the component Accordion should take care of this (rather than ProductAccordion) by allowing Accordion.Title to delegate the toggled event directly to its sibling Accordion.Content. How can I achieve this?
I would suggest storing the index of the open item in state, instead of a boolean. Then in your render, show={this.state.show} would be something like show={this.state.show === i}.
Full example:
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: null,
};
}
toggled(event, ind) {
const index = this.state.index;
this.setState({ show:ind === index ? null : ind });
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, i)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show === i}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
and this
class AccordionTitle extends Component {
constructor() {
super();
}
render() {
return (
<div style={this.props.style}>
<Button onClick={this.props.onToggled} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
I'm displaying a overlay page when a certain input is clicked. Now I want to remove that overlay page when a user clicks somewhere in that overlay. How can I do that?
I'm displaying the overlay on click like this
constructor(props) {
super(props);
this.state = {
showComponent: false,
};
this.popup_ques = this.popup_ques.bind(this);
}
popup_ques() {
this.setState({
showComponent: true,
});
}
render() {
return (
<div className="ff">
<div className="middle_div">
<input className='post_data_input' placeholder="Ask your question here" ref="postTxt" onClick={this.popup_ques}/>
</div>
{this.state.showComponent ? <QuestionOverlay/> : null}
</div>
);
}
My overlay is in the component QuestionOverlay
class QuestionOverlay extends Component {
constructor() {
super();
}
closeOverLay = (e) => {
alert("fse");
}
render() {
return (
//Here I have implemented my overlay
)
}
}
export default QuestionOverlay;
So how can I close/remove the overlay component when I click somewhere on my overlay?
Pass a function from the Overlay's parent component (the component which displays the Overlay) that is called onClick in the Overlay. This function will update this.state.showComponent of the parent to false to hide the Overlay.
Parent
constructor(props) {
super(props);
this.state = {
showComponent: false,
};
this.popup_ques = this.popup_ques.bind(this);
this.hide_overlay = this.hide_overlay.bind(this);
}
popup_ques() {
this.setState({
showComponent: true,
});
}
hide_overlay() {
this.setState({
showComponent: false
})
}
render() {
return (
<div className="ff">
<div className="middle_div">
<input className='post_data_input' placeholder="Ask your question here" ref="postTxt" onClick={this.popup_ques}/>
</div>
{this.state.showComponent && <QuestionOverlay hideOverlay={this.hide_overlay} />}
</div>
);
}
Overlay
class QuestionOverlay extends Component {
constructor() {
super();
}
closeOverLay = (e) => {
alert("fse");
}
render() {
return (
<div onClick={this.props.hideOverlay}>
// Overlay content
</div>
)
}
}
export default QuestionOverlay;