Close div in all child components - reactjs

What i am trying to do is this: i have multiple child components,
when i click on details i want to hide the same div in other child components
for example
when i click on div 1 close 2 and 3 and so on
<div onClick="showDetails()">component 1</div>
<div onClick="showDetails()">component 2</div>
<div onClick="showDetails()">component 3</div>
<details></details>
I have tried using ref, but ref did not work it only closes the first component div..
export default class Parent extends Component {
constructor(props) {
super(props)
this.myRef = React.createRef();
this.handleActivity = this.handleActivity.bind(this)
}
handleActivity() {
//call to a method in Child component
this.myRef.current.closeAll()
}
render() {
return (
<div>
<Child ref={this.myRef} closeAllOather={this.handleActivity} />
<Child ref={this.myRef} closeAllOather={this.handleActivity} />
<Child ref={this.myRef} closeAllOather={this.handleActivity} />
<Child ref={this.myRef} closeAllOather={this.handleActivity} />
</div>
)
}
}
export default class Child extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
this.show = this.show.bind(this)
this.hide = this.hide.bind(this)
}
closeAll(){
this.setState({show: false})
}
show() {
this.props.closeAllOather()
this.setState({ show: true })
}
hide() {
this.setState({ show: false })
}
render() {
return (
<div>
<div onClick={this.show} />
<div style={this.state.show ? visible : hidden}>
<div style={detailBlock}>
<span style={{ float: 'right' }} onClick={this.hide}>
close
</span>
{this.props.text}
<br />
</div>
</div>
</div>
)
}
}
Any suggestion on how this can be done?

I would keep the currentElement in Parent and verify in Child if it's active.
Example:
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
activeElement: null
};
this.onChildClick = this.onChildClick.bind(this)
}
onChildClick(e) {
this.setState({activeElement: e.currentTarget});
}
render() {
return (
<div>
<Child onClick={this.onChildClick} activeElement={this.state.activeElement} />
<Child onClick={this.onChildClick} activeElement={this.state.activeElement} />
<Child onClick={this.onChildClick} activeElement={this.state.activeElement} />
<Child onClick={this.onChildClick} activeElement={this.state.activeElement} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
const isActive = this.myRef.current && this.myRef.current === this.props.activeElement;
return (
<div ref={this.myRef} style={{color: isActive ? 'red' : 'black'}} onClick={this.props.onClick}>Child</div>
)
}
}
https://jsfiddle.net/n7y9buqg/5/

Change handleActivity in the Parent to
handleActivity(id) {
this.setState({
selectedChild:id
});
}
and the render in the parent to
render() {
return (
<div>
<Child isSelected={this.state.selectedChild === 1 ? "true":"false"} ref={this.myRef} closeAllOather={this.handleActivity(1)} />
<Child isSelected={this.state.selectedChild === 2 ? "true":"false"} ref={this.myRef} closeAllOather={this.handleActivity(2)} />
<Child ref={this.myRef} isSelected={this.state.selectedChild === 3 ? "true":"false"} closeAllOather={this.handleActivity(3)} />
<Child ref={this.myRef} isSelected={this.state.selectedChild === 4 ? "true":"false"} closeAllOather={this.handleActivity(4)} />
</div>
)
}
Finally in the child do:
render() {
return (
<div>
<div onClick={this.show} />
<div style={this.state.show ? visible : hidden}>
<div style={this.props.isSelected === "true" ? detailBlock:{display: "none"}}>
<span style={{ float: 'right' }} onClick={this.hide}>
close
</span>
{this.props.text}
<br />
</div>
</div>
</div>
)
}

Related

How to change state of a sibiling, if I click on a component?

I have three components that render a list of available timeslots.
If I click on a timeslot on the list of component1, it gets selected, now, If a sibiling component, let's call it component2, also has a timeslot that matches the one that had been clicked on component1, I want the one in component2 to be greyed out.
How can I do this?
The components that render the lists of available timeslots are called CompanyPanel:
export default class CompanyPanel extends React.Component {
constructor(props) {
super(props)
this.state = {
selectedTime: 'None',
times: this.props.times,
}
this.chooseTime = this.chooseTime.bind(this)
this.deleteTime = this.deleteTime.bind(this)
}
componentDidMount () {
this.chooseTime(this.state.selectedTime)
}
deleteTime (time) {
this.setState( ({times}) => ({
times: [...this.state.times].filter( t => t !== time),
}))
}
chooseTime (selectedTime) {
this.setState({
selectedTime,
})
}
render() {
const { selectedTime, times } = this.state
return (
<React.Fragment>
<div className="flex-auto pa3">
<div className="ba mv2">
<p className="tc pa2 dib bg-near-white">{this.props.name}</p>
</div>
<div className="ba mv2">
<p className="tc pa2 dib bg-red white">{selectedTime}</p>
</div>
<div className="ba mv2">
{times.map((time, i) => (
<div key={i} className="bg-green">
<span className="pa2 red pointer ma2 bg-white" onClick={() => this.deleteTime(time)}>X</span>
<p onClick={() => this.chooseTime(time.dateUTCString)} className="tc pa2 dib bg-yellow">{time.dateUTCString}</p>
</div>
))}
</div>
</div>
</React.Fragment>
)
}
}
And those CompanyPanel components are being wrapper by a parent component called CompaniesDashboard:
export default class CompaniesDashboard extends React.Component {
constructor(props) {
super(props)
this.state = {
error: null,
data,
}
this.isLoading = this.isLoading.bind(this)
}
isLoading() {
return this.state.posts === null && this.state.error === null
}
render() {
const { data, error } = this.state
return (
<React.Fragment>
{this.isLoading() && <p>LOADING</p>}
{error && <p>{error}</p>}
<div className="flex">
{data && data.map((company, i) => (
<CompanyPanel key={i} times={company.times} name={company.name} />
))}
</div>
</React.Fragment>
)
}
}
I think i need to somehow to set a state in the parent, when the chooseTime method is clicked inside if the CompanyPanel component. But not sure how to do it.

Cannot read property 'tabsDivIframe' of undefined - React

I am creating an application with tabs and divs to show the iframes or divs associated with the tabs. I have a navigation menu that works perfectly, when you click on one of the menu items you create a new tab and at the same time you should create a div / iframe (as applicable). The creation of the div is failing in my DivAndIframe class, it gives this error Can not read property 'tabsDivIframe' of undefined when I try to paint <DivAndIframe tabsDivIframe {this.props.divIframe.tabsDivIframe} />. It does not make sense because in my class App is an array with content that does not throw any errors.
class App extends Component {
constructor(props, context){
super(props, context);
["openTabs"].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
tabs:{
tabsLi: [],
},
divIframe:{
tabsDivIframe: [],
},
showtabs: true,
}
}
openTabs(e, url, iframe, trdtitle){
e.preventDefault();
//Change the state
this.setState({
showtabs: false,
})
//Creating tabs + iframe/div
if (this.state.tabs.tabsLi.includes(trdtitle) === false){
this.setState({
tabs: { tabsLi:[...new Set(this.state.tabs.tabsLi),trdtitle].filter(function(el) { return el; })},
divIframe: { tabsDivIframe:[...new Set(this.state.divIframe.tabsDivIframe),url].filter(function(el) { return el; })},
}, () => {
//this.state.divIframe.tabsDivIframe is an array
console.log(this.state.tabs.tabsLi);console.log(this.state.divIframe.tabsDivIframe)
})
}
}
render(){
return (
<>
<section className='section'>
<Tabs
navigation={this.state.navigation}
textvalue={this.state.textvalue}
showtabs={this.state.showtabs}
tabs={this.state.tabs}
tabsLi={this.state.tabs.tabsLi}
tabsDivIframe={this.state.divIframe.tabsDivIframe}
openTabs={this.openTabs}
removeTab={this.removeTab}
/>
</section>
</>
)
}
}
class Tabs extends Component {
render(){
return(
<div id="content-tabs" className="tabs">
{( this.props.showtabs)
? (
<>
<div className="waiting-leads">
<p>Parece que todavía no hay ningún lead...</p>
<h3>¡Ánimo, ya llega!</h3>
<img src={imgDinosaurio} alt="Dinosaurio"></img>
</div>
</>
) : (
<>
<ul id="resizable" className="content" >
<LiTabs
tabsLi={this.props.tabs.tabsLi}
removeTab={this.props.removeTab}
/>
</ul>
<DivAndIframe
tabsDivIframe={this.props.divIframe.tabsDivIframe}
/>
</>
)}
</div>
);
}
}
class LiTabs extends Component{
render(){
return(
<>
{this.props.tabsLi.map((value, index) =>
<li key={index}>
<span>{value}</span>
</li>
)}
</>
);
}
}
class DivAndIframe extends Component{
render(){
return(
<>
{this.props.tabsDivIframe.map((url, index) =>
<div key={index}>
<span>Test {url}</span>
</div>
)}
</>
);
}
}
I do not understand why DivAndIframe does not work when it is exactly the same as LiTabs
I think you have a typo.
When rendering Tabs, in App, you pass the props:
<Tabs
navigation={this.state.navigation}
textvalue={this.state.textvalue}
showtabs={this.state.showtabs}
tabs={this.state.tabs}
tabsLi={this.state.tabs.tabsLi}
tabsDivIframe={this.state.divIframe.tabsDivIframe}
openTabs={this.openTabs}
removeTab={this.removeTab}
/>
And inside Tabs you have:
<DivAndIframe
tabsDivIframe={this.props.divIframe.tabsDivIframe}
/>
You aren't passing divIframe to Tabs and that is why you are getting Can not read property 'tabsDivIframe' of undefined. this.props.divIframe is undefined.
Maybe it should be other name?
Like this.props.tabsDivIframe ?

How to return element in react class functions

How to return element in react class functions on a click. is it even possible?
class Item extends Component {
constructor(props) {
super(props);
this.itemInfo = this.itemInfo.bind(this);
}
itemInfo = () =>{
return <div> some info</div>
}
render(){
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
</div>
)
}
}
class Item extends React.Component {
state = {
showDiv: false
};
render() {
return (
<div>
<div
style={{ cursor: "pointer" }}
onClick={() =>
this.setState(prevState => ({
showDiv: !prevState.showDiv
}))
}
>
Click Me
</div>
{/*Show the INFO DIV ONLY IF THE REQUIRED STATE IS TRUE*/}
{this.state.showDiv && <InfoDiv />}
</div>
);
}
}
//This is the div which we want on click
var InfoDiv = () => (
<div style={{ border: "2px solid blue",borderRadius:10, padding: 20 }}>
<p> Long Text DIVLong Text DIVLong Text DIVLong Text DIVLong Text DIV </p>
</div>
);
ReactDOM.render(<Item />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You should do that in the state.
itemInfo = () =>{
this.setState({ component:<div> some info</div> });
}
and render the component like this
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
{this.state.component}
</div>
)
You can try something like this, using the state and conditional rendering:
class Item extends Component {
constructor(props) {
super(props)
this.state = {
showMore: false,
}
}
toggleShowMore = () => {
this.setState({ showMore: !this.state.showMore })
}
render() {
return (
<div>
<div onClick={this.toggleShowMore}>
{this.state.showMore ? 'Show less' : 'Show more'}
</div>
{this.state.showMore ? <div>some info</div> : null}
</div>
)
}
}
Here's how I would do it:
function ItemInfo() {
return(
<div>Some Info</div>
);
}
class Item extends Component {
constructor(props) {
super(props);
this.handleClick= this.handleClick.bind(this);
this.state = {
showInfo: false
}
}
handleClick() {
this.setState((prevState) => {showInfo: !prevState.showInfo});
}
render(){
return(
<div>
<div onClick={this.handleClick}> Click Here <div>
{ this.state.showInfo ?
<ItemInfo/>
: null }
</div>
)
}
}

Updating props in note taking app in React

I'm stuck on my note taking app. Basically the App component passes in data to the NoteEntry component through props. Yet I can't figure out how to edit the previous passed text through props within each NoteEntry instance when I click the "edit" button. The edit button is supposed to bring up text inputs to change the content by updating the text and then pressing the save button. Any tips on how to go about it?
class App extends Component {
constructor(props) {
super(props);
this.state = {
notes: [],
title: "",
details: ""
}
this.updateTitle = this.updateTitle.bind(this);
this.updateDetails = this.updateDetails.bind(this);
this.submitHandler = this.submitHandler.bind(this);
this.deleteHandler = this.deleteHandler.bind(this);
}
updateTitle(event) {
this.setState({ title: event.target.value });
}
updateDetails(event) {
this.setState({ details: event.target.value });
}
submitHandler(e) {
e.preventDefault();
if (!this.state.title.length || !this.state.details.length) {
return;
}
const newNote = {
newTitle: this.state.title,
newDetails: this.state.details
}
this.setState(prevState => ({
notes: prevState.notes.concat(newNote),
title: "",
details: ""
}))
}
deleteHandler(id) {
this.setState(prevState => ({
notes: prevState.notes.filter(el => el !== id)
}))
}
render() {
return (
<div className="container">
<h1 className="title">React Notes App</h1>
<NoteForm
titleValue={this.state.title}
detailsValue={this.state.details}
titleHandle={this.updateTitle}
detailsHandle={this.updateDetails}
onSubmit={this.submitHandler}
/>
<div className="entry-section">
{this.state.notes.map((note, i) => (
<NoteEntry
key={i}
title={note.newTitle}
details={note.newDetails}
deleteNote={this.deleteHandler.bind(this, note)}
/>
))}
</div>
</div>
);
}
}
const NoteForm = (props) => {
return (
<div>
<form className="form-section">
<input
className="title-input"
type="type"
placeholder="Title"
value={props.titleValue}
onChange={props.titleHandle}
/>
<br />
<textarea
className="details-input"
cols="20"
rows="3"
placeholder="Details"
value={props.detailsValue}
onChange={props.detailsHandle}
/>
<br />
<button
className="input-button"
onClick={props.onSubmit}
>Add Note</button>
</form>
</div>
)
}
class NoteEntry extends Component {
constructor(props) {
super(props);
this.state = {
display: false,
editTitle: this.props.title,
editDetails: this.props.details,
editing: false
}
this.displayToggle = this.displayToggle.bind(this);
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
displayToggle() {
this.setState(prevState => ({
display: !prevState.display
}))
}
edit() {
this.setState({
editing: true
})
}
save() {
let titleVal = this.refs.updateTitle.value;
let detailsVal = this.refs.updateDetails.value;
this.setState({
editTitle: titleVal,
editDetails: detailsVal,
editing: false
})
}
render() {
return (
<div className="entry">
<div className="entry-header" onClick={this.state.editing ? null : this.displayToggle}>
{this.state.editing ? (
<input ref="updateTitle" className="edit-title" type="text" />
) : (
<h2 className="entry-title">{this.props.title}</h2>
)}
<p className="timestamp">{this.displayTime}</p>
</div>
<hr />
<div className={"entry-content " + (!this.state.display ? "hide-details" : null)}>
{this.state.editing ? (
<textarea ref="updateDetails" className="edit-details" cols="10" rows="2"></textarea>
) : (
<p className="details">{this.props.details}</p>
)}
<div className="entry-buttons">
{this.state.editing ? (
<button className="save" onClick={this.save}>Save</button>
) : (
<button className="edit" onClick={this.edit}>Edit</button>
)
}
<button className="delete" onClick={this.props.deleteNote}>Delete</button>
</div>
</div>
</div>
)
}
}
You can do by pass data from child to parent component as mention it in comment.
In you case NoteEntry add onEditNote props. This props use for function by parent (App component) and use by onClick edit button.
<NoteEntry
...
onEditNote={this.handleClickEdit}
/>
then in class NoteEntry
<button className="edit" onClick={() => this.props.handleClickEdit(this.props.title, this.props.detail)}>Edit</button>
So, handleClickEdit handle by App component and set it to your state
handleClickEdit = (_title, _detail) => {
this.setState({title: _title, details: _detail});
}
Now, your NoteForm component able to edit.

React bootstrap panel custom heading- Expand collapse not working

I am trying to use React bootstrap panelgroup (Accordion)
I want a custom header with radio buttons. Hence i replaced header with my custom header. After using custom header, Expand collapse has stopped working.
Code (Custom header)
constructor(props) {
super(props);
this.state = {
isSelected: false,
};
}
componentWillMount () {
if (this.props.isSelected) {
this.state = {isSelected:true};
} else {
this.state = {isSelected: false};
}
}
componentWillUpdate () {
if (this.props.isSelected) {
this.state = {isSelected:true};
} else {
this.state = {isSelected: false};
}
}
render() {
let radio =
<span>
<input type="radio" className="accordion_checkbox" name={this.props.name} />
{this.props.header}
</span> ;
if (this.state.isSelected) {
radio = <span>
<input type="radio" className="accordion_checkbox" defaultChecked name={this.props.name} />
{this.props.header}
</span>
}
return (
<div>
{radio}
</div>
)
}
Panel:
<PanelGroup className="payment-accordion" activeKey={this.state.activeKey} onSelect={e => this.handleSelect(e)} accordion>
<Panel header={<PanelHeaderCustom name="saved_card" isSelected={this.state.activeKey === "savedCards"} header="SAVED CARD"/>} eventKey="savedCards">
<SwipableCards savedCards={this.props.savedCards}/>
</Panel>
<Panel header={<PanelHeaderCustom name="debit_card" isSelected={this.state.activeKey === "creditDebitCards"} header="DEBIT CARD"/>} eventKey="creditDebitCards">Debit/Credit Card</Panel>
<Panel header={<PanelHeaderCustom name="net_banking" isSelected={this.state.activeKey === "netbanking"} header="NET BANKING"/>} eventKey="netbanking">Debit/Credit Card</Panel>
</PanelGroup>
What am i missing?
Thanks in advance
When calling an element with JSX syntax, the generated object type doesn't have "children" as prop, and I think react-bootstrap Panel cannot handle that situation correctly.
<PanelHeaderCustom /> // returns Object { type: PanelHeaderCustom(), props: { header:"NET BANKING", isSelected: false, name: "net_banking" } }
Therefore, as a workaround, you could wrap your custom header to div in the Panel header prop:
class PanelHeaderCustom extends Component {
render () {
return (
<span>
<input
type='radio'
className='accordion_checkbox'
checked={this.props.isSelected}
name={this.props.name} />
{this.props.header}
</span>
)
}
}
export default class Test extends Component {
constructor (props) {
super(props)
this.state = {
activeKey: ''
}
}
handleSelect (e) {
this.setState({
activeKey: e
})
}
render () {
return (
<div>
<PanelGroup
className='payment-accordion'
activeKey={this.state.activeKey}
onSelect={(e) => this.handleSelect(e)}
accordion>
<Panel
header={
<div>
<PanelHeaderCustom
name='saved_card'
isSelected={this.state.activeKey === 'savedCards'}
header='SAVED CARD' />
</div>
}
eventKey='savedCards'>
<div>Example</div>
</Panel>
<Panel
header={
<div>
<PanelHeaderCustom
name='debit_card'
isSelected={this.state.activeKey === 'creditDebitCards'}
header='DEBIT CARD' />
</div>
}
eventKey='creditDebitCards'>
Debit/Credit Card
</Panel>
<Panel
header={
<div>
<PanelHeaderCustom
name='net_banking'
isSelected={this.state.activeKey === 'netbanking'}
header='NET BANKING' />
</div>
}
eventKey='netbanking'>
Debit/Credit Card
</Panel>
</PanelGroup>
</div>
)
}
}
ReactDOM.render(
<Test />,
document.getElementById('main')
)
As a side note, I think you should make your custom header a stateless component, because there is no point for the component to know it's own state. Just use props in the PanelHeaderCustom and control the instances with the main app's state.
When you make that component stateless, you can write your component's code a as a pure function without need for unnecessary div-wrapping:
// Returns { type: "span", props: { children: [ { type: "input"... }, "SAVED_CARD" ] } }
function PanelHeaderCustom (
header,
name,
isSelected
) {
return (
<span>
<input
type='radio'
className='accordion_checkbox'
checked={isSelected}
name={name} />
{header}
</span>
)
}
Then call the function in Panel's header prop:
<Panel
header={
PanelHeaderCustom(
'SAVED CARD',
'saved_card',
this.state.activeKey === 'savedCards'
)
}
eventKey='savedCards'>
<div>Example</div>
</Panel>

Resources