//child component
import React, { Component } from 'react'
const NavButton = ({ active, title, href, onSetActive }) => {
return (
<button
className={active ? "btn btn-light regular-btn active" : "btn btn-light regular-btn"}
href={href}
onClick={onSetActive} >
{title}
</button>
)
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
activeIndex: 1,
buttons: [
{
title: "1",
key: 0,
value: 1
},
{
title: "3",
key: 1,
value: 3
}
]
}
}
//It was changing active index
handleChangeActive(newActiveIndex) {
// console.log("Working");
this.setState({ activeIndex: newActiveIndex });
}
render() {
const { activeIndex } = this.state;
return (
<div>
<nav id="navbarMain">
<div className="navbar-nav flex-row">
{this.state.buttons.map((button, buttonIndex) =>
/* determine which nav button is active depending on the activeIndex state */
<NavButton onClick={() => this.investmentHandler.bind(this)} value={button.value} onSetActive={() => this.handleChangeActive(buttonIndex)} active={buttonIndex === activeIndex} title={button.title} key={button.key} />)}
</div>
</nav>
</div>
)
}
}
export default Child
// parent component
import React, { Component } from 'react'
import Child from './Child';
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
firstValue: 1,
secondValue: 3,
result: ''
}
// this.add = this.add.bind(this);
}
//calculating the input values
Add = () => {
var { firstValue, secondValue, result } = this.state;
result = firstValue + secondValue;
console.log(result);
document.getElementById("result").innerHTML = result;
}
render() {
return (
<>
//Child component is used inside the parent component using props
<Child investmentHandler={this.add} />
<p id="result">Result</p>
</>
)
}
}
export default Parent
I need the event handler(Add) has to work inside the child component.
How to use event handler using props in class components.
How to call the parent Method in child class component in react.
I was tried using props but it was not working.
Based on the child component input button it has to get the result.
On Child class your component NavButton has an onClick attribute that calls onSetActive, so you can call investmentHandler on the onSetActive function:
//child component
import React, { Component } from "react";
const NavButton = ({ active, title, href, onSetActive }) => {
return (
<button
className={
active
? "btn btn-light regular-btn active"
: "btn btn-light regular-btn"
}
href={href}
onClick={onSetActive}
>
{title}
</button>
);
};
class Child extends Component {
constructor(props) {
super(props);
this.state = {
activeIndex: 1,
buttons: [
{
title: "1",
key: 0,
value: 1,
},
{
title: "3",
key: 1,
value: 3,
},
],
};
}
//It was changing active index
handleChangeActive(newActiveIndex) {
// console.log("Working");
this.setState({ activeIndex: newActiveIndex });
this.props.investmentHandler();
}
render() {
const { activeIndex } = this.state;
return (
<div>
<nav id="navbarMain">
<div className="navbar-nav flex-row">
{this.state.buttons.map((button, buttonIndex) => (
/* determine which nav button is active depending on the activeIndex state */
<NavButton
value={button.value}
onSetActive={() => this.handleChangeActive(buttonIndex)}
active={buttonIndex === activeIndex}
title={button.title}
key={button.key}
/>
))}
</div>
</nav>
</div>
);
}
}
export default Child;
On the Parent class when getting the props from the Child class you was calling "this.add" when you should be calling "this.Add":
// parent component
import React, { Component } from 'react'
import Child from './Child';
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
firstValue: 1,
secondValue: 3,
result: ''
}
// this.add = this.add.bind(this);
}
//calculating the input values
Add = () => {
console.log('Im here')
var { firstValue, secondValue, result } = this.state;
result = firstValue + secondValue;
console.log(result);
document.getElementById("result").innerHTML = result;
}
render() {
return (
<>
<Child investmentHandler={this.Add} />
<p id="result">Result</p>
</>
)
}
}
export default Parent
I made this few changes and the code worked for me.
Check this might help
//child component
import React, { Component } from 'react'
const NavButton = ({ active, title, href, onSetActive }) => {
return (
<button
className={active ? "btn btn-light regular-btn active" : "btn btn-light regular-btn"}
href={href}
onClick={onSetActive} >
{title}
</button>
)
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
activeIndex: 1,
buttons: [
{
title: "1",
key: 0,
value: 1
},
{
title: "3",
key: 1,
value: 3
}
]
}
}
//It was changing active index
handleChangeActive(newActiveIndex) {
// console.log("Working");
this.setState({ activeIndex: newActiveIndex });
}
render() {
const { activeIndex } = this.state;
return (
<div>
<nav id="navbarMain">
<div className="navbar-nav flex-row">
{this.state.buttons.map((button, buttonIndex) =>
/* determine which nav button is active depending on the activeIndex state */
<NavButton onClick={() => this.props.investmentHandler()} value={button.value} onSetActive={() => this.handleChangeActive(buttonIndex)} active={buttonIndex === activeIndex} title={button.title} key={button.key} />)}
</div>
</nav>
</div>
)
}
}
export default Child
// parent component
import React, { Component } from 'react'
import Child from './Child';
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
firstValue: 1,
secondValue: 3,
result: ''
}
// this.add = this.add.bind(this);
}
//calculating the input values
Add = () => {
var { firstValue, secondValue, result } = this.state;
result = firstValue + secondValue;
console.log(result);
document.getElementById("result").innerHTML = result;
}
render() {
return (
<>
//Child component is used inside the parent component using props
<Child investmentHandler={this.Add} />
<p id="result">Result</p>
</>
)
}
}
export default Parent
Related
Class based app here.
MainComponent.js
export class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
nr: 0
}
}
btnClicked(e, nr, name){
this.setState({name: name});
this.setState({nr: nr});
}
render() {
return (
<div>
<div>
<button className="btn btn-success" onClick={e => this.btnClicked(e, '1', 'one')}>UNO</button>
<button className="btn btn-success" onClick={e => this.btnClicked(e, '2', 'two')}>DUE</button>
<button className="btn btn-success" onClick={e => this.btnClicked(e, '1', 'three')}>TRE</button>
</div>
<div>
{this.state.nr === '1' && <Child1 name={this.state.name}/>}
{this.state.nr === '2' && <Child2 name={this.state.name}/>}
</div>
</div>
)
}
}
export default MainComponent;
And then here are the 2 child-components:
Child1.js
export class Child1 extends Component {
constructor(props) {
super(props);
this.state = {
name: props.name,
}
}
render() {
return (
<div>
<div>
{
this.state.name !== '' &&
<h3>{this.state.name}</h3>
}
</div>
</div>
)
}
}
export default Child1;
Child2.js
export class Child2 extends Component {
constructor(props) {
super(props);
this.state = {
name: props.name,
}
}
render() {
return (
<div>
<div>
{
this.state.name !== '' &&
<h3>{this.state.name}</h3>
}
</div>
</div>
)
}
}
export default Child2;
When I click to the button "One" I see the text "One". But when clicking on button "Three" I still see the same text "One".
I have tried adding forceUpdate but it didnt help:
btnClicked(e, nr, name){
this.forceUpdate(function(){
this.setState({name: name});
this.setState({nr: nr});
});
}
How can I instruct the app to rerender the child component at each click?
In this case you should not assign the Child components prop value to state. Just use the prop value in the component.
export class Child1 extends Component {
render() {
return (
<div>
<div>
{
this.props.name !== '' &&
<h3>{this.props.name}</h3>
}
</div>
</div>
)
}
}
export default Child1;
The Child component's constructor will not be called each time the props update. So, state will not be updated when the props are updated.
In this case - as in most cases - you do not need forceUpdate.
I have a Tabbar in my Tabbar Component, Which I Change the index props in it :
class Tabbar extends Component {
state = {
index: this.props.index,
name: this.props.name,
image: this.props.image
};
changeTabs = () => {
this.setState({index: this.props.index});
}
render() {
return (
<React.Fragment>
<div id={this.state.index} className="col">
<button onClick={this.changeTabs}></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And Then In my Other Component, I Wanna Re-Render a fragment after props change. Here's my Code :
import Tabbar from './Tabbar';
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
{index: 0, name: "tab0", image:require('../Assets/profile.svg'),childView: {ProfilePage} },
{index: 1, name: "tab1", image:require('../Assets/home.svg'),childView: {HomePage}},
{index: 2, name: "tab2", image:require('../Assets/blog.svg'),childView: {BlogPage}},
],
}
}
handleRender = () => {
this.state.tabs.map(item => {
if (item.index === this.props.index) {
return <item.childView/>;
}
})
return <BlogPage/>;
}
render() {
return (
<div>
<Header/>
{this.handleRender()}
{this.state.tabs.map(item =>
<Tabbar key={item.index} index={item.index} name={item.name} image={item.image}/>
)}
</div>
);
}
}
export default Tabview;
The Method "handleRender" should handle the rendering.
I tried to use "componentDidMount" or "componentDidUpdate", But I didn't work.
How Can I Make it Work?
Thank you in advance!
You dont need to have a state in the child component for this reason
You can simply have a callback in parent and call it in child component like below.
import React, { Component } from "react";
class Tabbar extends Component {
render() {
return (
<React.Fragment>
<div id={this.props.index} className="col">
<button
onClick={() => this.props.changeTabs(this.props.index)}
></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And in parent you maintain the active index state
import Tabbar from "./Tabbar";
import React, { Component } from "react";
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
//your tabs
],
activeIndex: 0
};
}
handleRender = () => {
this.state.tabs.map((item) => {
if (item.index === this.state.activeIndex) {
return <item.childView />;
}
});
return <div />;
};
render() {
return (
<div>
{this.handleRender()}
{this.state.tabs.map((item) => (
<Tabbar
key={item.index}
index={item.index}
name={item.name}
image={item.image}
changeTabs={(index) => this.setState({ activeIndex: index })}
/>
))}
</div>
);
}
}
export default Tabview;
I created a project in Reactjs where I have a list of names that I want to display in a custom list. Each item has a button to delete the item, however whenever I click the button, the last item is removed from the list no matter which list item I click.
I have already tried to debug my code using the js-console but that made the problem even stranger since the console displays the correct state wheras the component "List" renders a list item which is no longer present in the state object
import React, { Component } from 'react';
import './ListItem'
import ListItem from './ListItem';
class List extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{name: 'Tobi'},
{name: 'Maxi'},
{name: 'David'},
{name: 'Peter'},
]
}
}
removeItem = (id) => {
let few = this.state.items;
few.splice(id,1);
//console.log(this.state.items);
this.setState({items: few}, function(){
console.log(this.state.items.map((item) => item.name));
this.forceUpdate();
});
}
render() {
return (
<div>
<ul>
{this.state.items.map((item, i) => <ListItem name={item.name} key={i} id={i} remove={this.removeItem}/>)}
</ul>
</div>
);
}
}
import React, { Component } from 'react';
class ListItem extends Component {
constructor(props) {
super(props);
this.state = {
name: this.props.name,
id: this.props.id
}
}
test = () => {
this.props.remove(this.state.id);
}
render() {
return (
<li>{this.state.name} <button onClick={() => this.test()}>click me</button></li>
);
}
}
export default ListItem;
As is said i excpected the right list item to be removed however it is always the last item that isnt rendered anymore even though the state object says different.
The main problem is that you're using an array index as a key. When you first render the ListItems you have :
ListItem name={'Tobi'} key={0}
ListItem name={'Maxi'} key={1}
ListItem name={'David'} key={2}
ListItem name={'Peter'} key={3}
Let's say you removed the item with index 1, all other items will shift index:
ListItem name={'Tobi'} key={0}
ListItem name={'David'} key={1}
ListItem name={'Peter'} key={2}
React will only compare the keys, and because the only difference between the first and second render is that the item with key={3} is not present, this is the item that will be removed from the dom.
Also avoid mutating the state directly (few.splice(id,1)), and try to avoid this.forceUpdate()
Try using an actual id in your data :
import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class List extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, name: "Tobi" },
{ id: 2, name: "Maxi" },
{ id: 3, name: "David" },
{ id: 4, name: "Peter" }
]
};
}
removeItem = id => {
let few = this.state.items.filter(item => item.id !==id);
//console.log(this.state.items);
this.setState({ items: few }, function() {
console.log(this.state.items.map(item => item.name));
//this.forceUpdate();
});
};
render() {
return (
<div>
<ul>
{this.state.items.map((item, i) => (
<ListItem
name={item.name}
key={item.id}
id={item.id}
remove={this.removeItem}
/>
))}
</ul>
</div>
);
}
}
class ListItem extends Component {
constructor(props) {
super(props);
this.state = {
name: this.props.name,
id: this.props.id
};
}
test = () => {
this.props.remove(this.state.id);
};
render() {
return (
<li>
{this.state.name} <button onClick={() => this.test()}>click me</button>
</li>
);
}
}
function App() {
return (
<div className="App">
<List />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
My current structure is like this:
state={
activetab=1,
activetab2=2}
<child1 activetab={activetab}/>
<child2 activetab2={activetab2}/>
And I would like when the user changes the active tab from child 2 to update the state on the parent component, and then also handle the change on the child1 component.
child 1 code :
render() {
const activetab=this.props;
this.showimage1 = () => {
if (activetab === 1) {
return (
<img src={slide1} alt='' />
)
} else null;
}
return (
<div>
{this.showimage1()}
</div>
child 2 code :
constructor (props) {
super(props)
this.changetab= this.changetab.bind(this)
this.state={
activetab:1}
}
render () {
const activetab2=this.props;
changetab(tab) {
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
})
}
}
return(
<div>
<button onClick={() => {
this.changetab(1)
}}
>tab1 </button>
</div>
So as you can see I want when the user changes the current tab from child 2 to pass the props in my parent component so that the child1 component knows which image to show depending on the tab. Anyone knows how to do it without rewriting everything in a single component?
Create a main component that renders the two component child 1 and 2 create only 1 source of truth means only 1 state to be pass to the child components as props.
export class MainComponent extends Component {
constructor (props) {
super(props)
this.changetab = this.changetab.bind(this)
this.state={
activetab: 0
}
}
changetab(tab) {
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
})
}
render() {
return (
<div>
<ChildOne activetab={this.state.activetab} />
<ChildTwo activetab={this.state.activetab} changetab={this.changetab} />
</div>
)
}
}
// ChildOne Component pass state activetab as props
render() {
const { activetab } = this.props;
this.showimage1 = () => {
if (activetab === 1) {
return (
<img src={slide1} alt='' />
)
} else null;
}
return (
<div>
{this.showimage1()}
</div>
// ChildTwo Component
render () {
const { activetab, changetab } = this.props;
return(
<div>
<button onClick={() => {
this.changetab(1)
}}
>tab1 </button>
</div>
The parent component must have 2 function handlechange that you pass to your childs.
Will look something like that
// Parent
class Parent extends React.Component {
// Constructor
constructor(props) {
this.state = {
child1: 1,
child2: 0
};
}
handleChangeChild1(state) {
this.setState({ child1: state });
}
handleChangeChild2(state) {
this.setState({ child2: state });
}
render() {
return (
<div>
<Child1 onXChange={this.handleChangeChild1} />
<Child2 onYChange={this.handleChangeChild2} />
</div>
);
}
}
And in the children you do
class Child extends React.Component {
constructor(props) {
super(props)
}
handleChange(e) = () => {
this.props.onXChange
}
render() {
return (
<input onChange={this.handleChange} />
)
}
}
I recommend you read the doc
You can wrap the components in a parent component. Then you can pass a function from the parent to child 2 by attaching it to a prop. Child 2 can then call this function on a change.
In the parent:
render() {
const someFunction = (someVariable) => {
// do something
}
return(
<child2 somePropName={ this.someFunction } />
)
}
edit: removed .bind() from this example
I have to render several tabs on my dashboard, where I can navigate between them. Each tab must be reusable. In my sample code, I have 2 tabs and if I click on a tab, the matching tab renders. I just don't know if I am reasoning in the right way. How can I do this? My current Code is this:
import React, {Component} from 'react';
function SelectTab(props) {
var tabs = ['Overview', 'Favorites'];
return (
<ul>
{tabs.map(function(tab){
return (
<li key={tab} onClick={props.onSelect.bind(null, tab)}>
{tab}
</li>
)
}, this)}
</ul>
)
}
function Content(props) {
return (
<div>Content {props.tab}</div>
)
}
export default class MainContent extends Component {
constructor(props){
super(props);
this.state = {
selectedTab: 'Overview'
}
this.updateTab = this.updateTab.bind(this);
}
componentDidMount() {
this.updateTab(this.state.selectedTab);
}
updateTab(tab){
this.setState(function(){
return {
selectedTab: tab
}
});
}
render(){
return (
<div>
<SelectTab selectedTab={this.state.selectedTab} onSelect={this.updateTab}/>
<Content tab={this.state.selectedTab}/>
</div>
)
}
}
You did almost good. But I would rather render Content in SelectTab component as its already getting selectedTab prop, this way you can render specific content based on that prop. Also:
componentDidMount() {
this.updateTab(this.state.selectedTab);
}
is not necessary as state is already set.
Refactored example:
import React, { Component } from 'react';
function SelectTab(props) {
var tabs = ['Overview', 'Favorites'];
return (
<div>
<ul>
{tabs.map(function (tab) {
return (
<li key={tab} onClick={props.onSelect.bind(null, tab)}>
{tab}
</li>
)
}, this)}
</ul>
<Content tab={props.selectedTab}/>
</div>
)
}
function Content(props) {
return <div>Content {props.tab}</div>
}
export default class MainContent extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: 'Overview'
};
this.updateTab = this.updateTab.bind(this);
}
updateTab(tab) {
this.setState(function () {
return {
selectedTab: tab
}
});
}
render() {
return <SelectTab selectedTab={this.state.selectedTab} onSelect={this.updateTab}/>;
}
}
Also keep in mind that with proper babel-transpiling your code could be much simplified like this:
import React, { Component } from 'react';
const TABS = ['Overview', 'Favorites'];
const SelectTab = ({ selectedTab, onSelect }) => (
<div>
<ul>
{
TABS.map(
tab => <li key={tab} onClick={() => onSelect(tab)}> {tab} </li>
)
}
</ul>
<Content tab={selectedTab}/>
</div>
);
const Content = ({ tab }) => <div>Content {tab}</div>;
export default class MainContent extends Component {
state = {
selectedTab: 'Overview'
};
updateTab = tab => this.setState(() => ({ selectedTab: tab }));
render() {
return <SelectTab selectedTab={this.state.selectedTab} onSelect={this.updateTab}/>;
}
}