On the parent component, I am trying to do 2 buttons, each of them will show a component, the first button shows itemlist and second component shows itemlist2 but I couldn't seem to get it right, I tried to follow this example (https://codepen.io/PiotrBerebecki/pen/yaVaLK) even though I'm not sure its the right resource for such feature, here is my app.js code
class App extends Component {
state = {
one: false
};
handleClick(e) {
const userChoice = e.target.className;
this.setState({
userChoice
});
}
toggleDiv() {
this.setState({
one: !this.state.one
});
}
toggleDiv1() {
this.setState({
one: this.state.one
});
}
render() {
return (
<div>
<NavBar />
<div className="container-fluid">
<ServiceSelector toggleDiv={this.toggleDiv.bind(this)} toggleDiv1=
{this.toggleDiv1.bind(this)} />
{this.state.one == false ? <ItemList /> : <ItemList2 />}
</div>
</div>
);
}
}
class ServiceSelector extends React.Component {
toggleDiv() {
this.props.toggleDiv();
}
toggleDiv2() {
this.props.toggleDiv2();
}
render() {
return (
<div>
{" "}
<button onClick={this.toggleDiv.bind(this)}>sss </button>
<button onClick={this.toggleDiv1.bind(this)}>sss </button>
</div>
);
}
}
Actually, this function
toggleDiv1() {
this.setState({
one: this.state.one
});
}
is useless.
The toggle function should have one
toggleFunc() {
this.setState({stateWatched = !this.state.stateWatched})
}
Use this function in both case(set true or false). And don't bind when call in ServiceSelector component, this is nonesense.
class App extends Component {
state = {
one: false
};
handleClick(e) {
const userChoice = e.target.className;
this.setState({
userChoice
});
}
toggleDiv() {
this.setState({
one: !this.state.one
});
}
render() {
return (
<div>
<NavBar />
<div className="container-fluid">
<ServiceSelector toggleDiv={this.toggleDiv.bind(this)} />
{this.state.one == false ? <ItemList /> : <ItemList2 />}
</div>
</div>
);
}
}
class ServiceSelector extends React.Component {
render() {
return (
<div>
{" "}
<button onClick={this.props.toggleDiv}>sss </button>
</div>
);
}
}
If you want to have 2 buttons to handle toggle. Change the logic of the function.
function toggleTrue() {
this.setState({one: true})
}
function toggleFalse() {
this.setState({one: false})
}
and then pass it like normal (remember to remove bind function in a child component )
I am not too sure about what you trying to do but i am gonna give it a try.
class App extends Component {
state = {
one: false
};
handleClick(e) {
const userChoice = e.target.className;
this.setState({
userChoice
});
}
toggleDiv = () => {
this.setState({
one: !this.state.one
});
}
toggleDiv1 = () => {
this.setState({
one: this.state.one
});
}
render() {
return (
<div>
<NavBar />
<div className="container-fluid">
<ServiceSelector toggleDiv={this.toggleDiv} toggleDiv1={this.toggleDiv1} />
{this.state.one == false ? <ItemList /> : <ItemList2 />}
</div>
</div>
);
}
}
class ServiceSelector extends React.Component {
render() {
return (
<div>
{" "}
<button onClick={this.props.toggleDiv}>sss </button>
<button onClick={this.props.toggleDiv1}>sss </button>
</div>
);
}
}
Using arrow functions remove the need of typing this.bind as it does the binding for you
Let me know if it helps
Related
class App extends React.Component{
state = {
number:0
};
ADD = ()=>{
this.setState(current=>current.number+1)
};
MINUS = ()=>{
this.setState(current=>--current.number);
};
render(){
return(
<div>
<h1>Number is: {this.state.number}</h1>
<button onClick={this.ADD}>Add</button>
<button onClick={this.MINUS}>Minus</button>
</div>
);
}
}
Code above is class component that use in ReactDOM.render(<App/>,docment.querySelector('#root'));
current=>--current.number works well but current=>current.number-1 doesn't work
I can't catch the difference between two
Also, I want to know what setState method dose when it takes foo as argument such this.setState(foo)
i've made a little changes and it works
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 0
};
}
ADD() {
this.setState(prev =>({number: prev.number + 1}))
}
MINUS() {
this.setState(prev =>({number: prev.number - 1}));
}
render() {
return (
<div >
<h1> Number is: {
this.state.number
}
</h1>
<button onClick = {
()=>this.ADD()
} > Add < /button>
<button onClick = {
()=>this.MINUS()
} > Minus </button>
</div>
);
}
}
ReactDOM.render(
<Counter/> ,
document.getElementById('app')
);
<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="app"></div>
I have a Parent(blue box with three buttons) and a Child(red box that shows content) component, that render some text based on the Child's state. Basically, the rendered view is this:
The Child component:
class Child extends Component {
constructor(props) {
super(props);
this.state = {
tab: this.props.tab
}
}
render() {
let text;
if (this.props.tab === '1') {
text = <div>text 1 </div>
} else if (this.props.tab === '2') {
text = <div>text 2 </div>
} else if (this.props.tab === '3') {
text = <div>text 3 </div>
}
return (
<div>
{text}
</div>
);
}
}
export default Child;
The Parent component:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
tab: null
}
this.onOneClik = this.onOneClik.bind(this);
this.onTwoClik = this.onTwoClik.bind(this);
this.onThreeClick = this.onThreeClick.bind(this);
}
onOneClik(e) {
this.setState({ tab: '1' });
}
onTwoClik(e) {
this.setState({ tab: '2' });
}
onThreeClick(e) {
this.setState({ tab: '3' });
}
render() {
return (
<div>
<button type="button" onClick={this.onOneClik}>1</button>
<button type="button" onClick={this.onTwoClik}>2</button>
<button type="button" onClick={this.onThreeClik}>3</button>
</div>
<div>
<Child tab={this.state.tab} />
</div>
);
}
}
The issue is that I need to re-render the Child when the button is clicked, but the Child shows only the content of the button which was clicked first, even though the state of the Parent updates itself when different buttons are clicked.
What would be the way to dynamically render the Child without involving Links? I guess redux could help, but I'm not sure on how to wrap redux around this.
A component will be re-rendered when its state or props changes. Instead of storing the initial tab prop in the state of the Child component, you can use the prop directly instead (you also have a few typos that are fixed below).
Example
class Child extends React.Component {
render() {
let text;
if (this.props.tab === "1") {
text = <div> text 1 </div>;
} else if (this.props.tab === "2") {
text = <div> text 2 </div>;
} else if (this.props.tab === "3") {
text = <div> text 3 </div>;
}
return <div>{text}</div>;
}
}
class Parent extends React.Component {
state = {
tab: null
};
onOneClick = e => {
this.setState({ tab: "1" });
};
onTwoClick = e => {
this.setState({ tab: "2" });
};
onThreeClick = e => {
this.setState({ tab: "3" });
};
render() {
return (
<div>
<button type="button" onClick={this.onOneClick}>
1
</button>
<button type="button" onClick={this.onTwoClick}>
2
</button>
<button type="button" onClick={this.onThreeClick}>
3
</button>
<Child tab={this.state.tab} />
</div>
);
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.4.1/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16.4.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am new to both coding as well as React.js, so any assistance in learning what I am doing incorrectly is greatly appreciated! I am creating multiple cards on a page with riddles where the answer is hidden via css. I am using an onClick function ("toggleAnswer") to toggle the state of each answer to change the className so that the answer will either be visible or hidden. Currently, the onClick event is changing the state for all the answers. I realize this is because my code is not targeting a particular element, but I am unsure how this can be done. How can this be achieved? My code is currently like this:
// RiddlesPage where toggleAnswer function is defined
class RiddlesPage extends Component {
constructor(props) {
super(props);
this.state = {
questionData: [],
isHidden: true
};
this.getPageData = this.getPageData.bind(this);
this.toggleAnswer = this.toggleAnswer.bind(this);
}
getPageData() {
console.log("we hit getPageData function starting --");
helpers.getRiddlesPage().then(data => {
console.log("this is the result", data);
this.setState({
questionData: data[0].questionData,
});
});
}
toggleAnswer(e) {
this.setState({ isHidden: !this.state.isHidden });
}
componentWillMount() {
this.getPageData();
}
render() {
const answerClass = this.state.isHidden ? "answer-hide" : "answer";
return (
<div>
<Riddles>
{this.state.questionData.map((data, index) => {
return (
<RiddlesItem
key={index}
id={index}
question={data.question}
answer={data.answer}
button={data.buttonURL}
answerClass={answerClass}
onClick={this.toggleAnswer}
/>
);
})}
</Riddles>
</div>
);
}
}
export default RiddlesPage;
// Riddles Component
import React from "react";
import "./riddles.css";
const Riddles = props => (
<div id="riddles-row">
<div className="container">
<div className="row">
<div className="col-12">
<div>{props.children}</div>
</div>
</div>
</div>
</div>
);
export default Riddles;
// RiddlesItem Component where onClick function is set as a prop
import React from "react";
import "./riddles.css";
const RiddlesItem = props => (
<div>
<div className="card-body">
<p id="question">{props.question}</p>
<img
className="img-fluid"
id={props.id}
src={props.button}
onClick={props.onClick}
alt="answer button"
/>
<p className={props.answerClass}> {props.answer} </p>
</div>
</div>
);
export default RiddlesItem;
You'd have to keep track of each answer that has been shown in state (in an array or something).
First
Send the index of the answer up in the onclick function. In that function, check if it exists in the "shownAnswers" array and either add or remove it.
onClick={e => props.onClick(e, props.id)}
and
toggleAnswer(e, index) {
if (this.state.shownAnswers.indexOf(index) > -1) {
this.setState({
shownAnswers: this.state.shownAnswers.filter(val => val !== index)
});
} else {
this.setState({
shownAnswers: this.state.shownAnswers.concat(index)
});
}
}
Then
When you're passing the class name down to the child component, check if its index is in the "shownAnswers" array to decide which class name to pass.
answerClass={this.state.shownAnswers.indexOf(index) > -1 ? "answer" : "answer-hide"}
Building off your example, it could look something like this (untested):
// RiddlesPage where toggleAnswer function is defined
class RiddlesPage extends Component {
constructor(props) {
super(props);
this.state = {
questionData: [],
shownAnswers: []
};
this.getPageData = this.getPageData.bind(this);
this.toggleAnswer = this.toggleAnswer.bind(this);
}
getPageData() {
console.log("we hit getPageData function starting --");
helpers.getRiddlesPage().then(data => {
console.log("this is the result", data);
this.setState({
questionData: data[0].questionData,
});
});
}
toggleAnswer(e, index) {
if (this.state.shownAnswers.indexOf(index) > -1) {
this.setState({ shownAnswers: this.state.shownAnswers.filter(val => val !== index) });
} else {
this.setState({ shownAnswers: this.state.shownAnswers.concat(index) });
}
}
componentWillMount() {
this.getPageData();
}
render() {
return (
<div>
<Riddles>
{this.state.questionData.map((data, index) => {
return (
<RiddlesItem
key={index}
id={index}
question={data.question}
answer={data.answer}
button={data.buttonURL}
answerClass={this.state.shownAnswers.indexOf(index) > -1 ? "answer" : "answer-hide"}
onClick={this.toggleAnswer}
/>
);
})}
</Riddles>
</div>
);
}
}
export default RiddlesPage;
// Riddles Component
import React from "react";
import "./riddles.css";
const Riddles = props => (
<div id="riddles-row">
<div className="container">
<div className="row">
<div className="col-12">
<div>{props.children}</div>
</div>
</div>
</div>
</div>
);
export default Riddles;
// RiddlesItem Component where onClick function is set as a prop
import React from "react";
import "./riddles.css";
const RiddlesItem = props => (
<div>
<div className="card-body">
<p id="question">{props.question}</p>
<img
className="img-fluid"
id={props.id}
src={props.button}
onClick={e => props.onClick(e, props.id)}
alt="answer button"
/>
<p className={props.answerClass}> {props.answer} </p>
</div>
</div>
);
export default RiddlesItem;
I have table and action icon 'delete' for example:
http://joxi.ru/gmvzWx4CxKbX5m
After click on icon, in my Redux Action i get TR and add class 'adm-pre-removed'
import * as types from '../constant/domain.const.js';
export function deleteDomain(event){
return (dispatch, getState) => {
(async () => {
let domainIdElement = event.target.closest('tr'),
id = domainIdElement.getAttribute('data-id');
domainIdElement.classList.add('adm-pre-removed');
setTimeout(() => {
dispatch({
type: types.REMOVE_DOMAIN_SUCCESS,
id: id
})
}, 3000)
})()
}
}
The problem is after the dispatch state is triggered and the render happens but the class remains.
I know that this is not a react way. And I really do not want to create a property in an article and to control this class is too much code. And to all this class should appear when clicking and disappear when the state has changed.
You can of course create another action and order the event onMouseDown but this is also a lot of code.
Which option is correct and how best to do it?
Render (Controll view)
function mapStateToProps (state) {
return {...state.menu}
}
function mapDispatchToProps(dispatch){
return {
...bindActionCreators({
...listMenu,
...deleteMenu,
...addMenu,
...removeError
}, dispatch),
dispatch
}
}
class Menu extends React.Component {
constructor (props, context) {
super(props, context);
}
componentDidMount() {
this.props.listMenu()
}
componentDidUpdate(){
//FormHelper.reactReRender('.adm-domain-form');
}
render() {
return (<div>
{this.props.errorMessage ? <Notification close={this.props.removeError} errorMessage={this.props.errorMessage} /> : ""}
<Sidebar />
<MenuPreview {...this.props} />
</div>);
}
}
Menu.contextTypes = {
router: PropTypes.object
}
export default connect(mapStateToProps, mapDispatchToProps)(Menu);
Render Component:
class MainEventsView extends React.Component {
render(){
return (
<div className="adm-content">
<Header />
<div className="container-fluid adm-card-head">
<Add {...this.props} />
<List {...this.props} />
</div>
</div>
);
}
}
export default MainEventsView;
Render List:
class ListMenu extends React.Component {
render(){
return(
<div className="col-xs-12 col-sm-12 col-md-6 col-lg-6">
<div className="adm-card adm-card-table">
<div className="table-responsive">
{this.props.menu && this.props.menu.length > 0 ?
<Table {...this.props} /> :
<p className="adm-card-table__text-no-available turn-center">Available menu are not found</p>
}
</div>
</div>
</div>
)
}
}
export default ListMenu;
Render Table:
class Table extends React.Component {
render () {
return (
<table className="adm-users-table__wrapper-table">
<Thead {...this.props} />
<Tbody {...this.props} />
</table>
);
}
}
export default Table;
Render Tbody:
class Cell extends React.Component {
render () {
let usersTemplate = this.props[this.props.name].map((item, i) => {
return (
<tr key={i} data-id={item.id}>
{this.template(item)}
</tr>
);
});
return (
<tbody>
{usersTemplate}
</tbody>
);
}
template (items) {
let keyArray = Object.keys(items),
uniqKey,
field,
userCell = keyArray.map((item, i) => {
uniqKey = i;
field = items[item] instanceof Object ? _.values(items[item]).join(', ') : items[item];
return (
<td key={i} className="adm-users-table__wrapper-table-data relative-core">{field}</td>
);
});
userCell.push(<Actions key={uniqKey + 1} id={items.id} {...this.props} />)
return userCell;
}
}
export default Cell;
Maybe, it could be. React renders elements in virtual dom and update changed elements in real-dom.
So your class is remained still.
I think that you have to figure out another way.
I have a component built using the below code. The aim is to add a class on the card to highlight it when the button inside it is clicked. However, the below code works on the first click but doesn't work for the subsequent clicks.
I understood that I have to set the clicked state of other elements to false when I remove the class. How can this be done?
import React, { Component } from 'react';
import './PricingCard.css';
class PricingCard extends Component {
constructor(){
super();
this.state = {
clicked : false
}
}
makeSelection(){
let elems = document.getElementsByClassName('Card');
for(var i=0;i<elems.length;i++){
elems[i].classList.remove("active");
}
this.setState({clicked: true});
}
render() {
var activeClass = this.state.clicked ? 'active' : '';
return (
<div className= {"categoryItem Card " + this.props.planName + " " +activeClass}>
<div className="cardDetails">
<div> {this.props.planName} </div>
<div className="pricing"> {this.props.price} </div>
<button onClick={this.makeSelection.bind(this)} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {this.props.footerText} </div>
</div>
</div>
);
}
}
export default PricingCard;
Wouldn't it be easier to have the logic in a parent component? Since it is "aware" of all the child Card components.
Have something like...
this.state = { selectedComponent: null };
onClick(card_id) {
this.setState({ selectedComponent: card_id });
}
...in render:
const cards = smth.map((card) =>
<Card onClick={this.onClick.bind(this, card.id)}
isActive={map.id === this.state.selectedComponent} />
Would this work?
Best way will be to lift lift the state up. Like this:
class PricingCardContainer extends React.Component {
constructor(props){
super(props);
this.state = {
selectedCard: NaN,
}
}
handleCardClick(selectedCard){ this.setState({ selectedCard }); }
render() {
return (
<div>{
this.props.dataArray.map((data, i) =>
<PricingCard
key={i}
className={this.state.selectedCard === i ? 'active': ''}
price={data.price}
onClick={() => this.handleCardClick(i)}
footerText={data.footerText}
planName={data.planName}
plan={data.plan}
/>
)
}</div>
)
}
}
const PricingCard = ({ className = '', planName, price, onClick, footerText }) => (
<div className= {`categoryItem Card ${planName} ${className}`}>
<div className="cardDetails">
<div> {planName} </div>
<div className="pricing"> {price} </div>
<button onClick={onClick} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {footerText} </div>
</div>
</div>
);
export default PricingCard;
Although it would be better to use some data id than index value.