I'm building a kind of shopping cart in React. I set some props in the child component and then passed state into them when they are used in the parent component. There are several things that I want to do, but to begin I want to update the quantity of the item when the buttons are pressed. I know how this would work if it were all coded in the parent component, but I'm trying to create a child component in order to make the code more efficient and to figure out how components interact with each other.
Can the props of the child be manipulated with onClick events here, and be used to change the state of different things?
If so, how do I do that?
Is there a logical way of creating this, or is there a much simpler way?
I know that Redux uses state management, but I want to figure this out in React for now.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
applesCount: 0,
orangesCount: 0,
peachesCount: 0,
grapesCount: 0,
applesPrice: 0.00,
orangesPrice: 0.00,
peachesPrice: 0.00,
grapesPrice: 0.00
}
}
render() {
return (
<>
<h1 className="text-center">Grocery Store</h1>
<div className="row text-center m-2">
<div className="col border border-primary m-2">
<Item fruitName='Apples' quantity={this.state.applesCount} price={this.state.applesPrice}/>
</div>
<div className="col border border-primary m-2">
<Item fruitName='Oranges' quantity={this.state.orangesCount} price={this.state.orangesPrice}/>
</div>
<div className="col border border-primary m-2">
<Item fruitName='Peaches' quantity={this.state.peachesCount} price={this.state.peachesPrice}/>
</div>
<div className="col border border-primary m-2">
<Item fruitName='Grapes' quantity={this.state.grapesCount} price={this.state.grapesPrice}/>
</div>
</div>
</>
)
}
}
class Item extends React.Component {
render() {
return (
<>
<h1>{this.props.fruitName}</h1>
<div>picture here</div>
<div className="row d-flex justify-content-center">
<button>-</button>
<h1>{this.props.quantity}</h1>
<button>+</button>
</div>
<h2>${this.props.price}</h2>
</>
)
}
}
export default App;
Pass down a callback to the child.
const Parent = () => {
const [value, setValue] = useState('')
return <Child setValue={setValue}/>
}
...
const Child = ({setValue}) => {
// use setValue here which will update parent state
}
you can do many type of solution for this
passing method wich that do setState to child and call it in there then you can update the state of parent wich gets from props.
const Parent = () => {
const [value, setValue] = useState('')
return ();
}
const Child = ({setValue}) => {}
do it with redux or another state management libraries .
Related
I'm currently building a react app which has a component slider and I need to pass the data back to the parent, the only fact is that the child is a little bit complex hook and I've been unable to find something similar that can help me to implement on my project, this is what I have:
The child
function valuetext(value) {
return `${value}`;
}
export default function RangeSlider() {
const classes = useStyles();
const [value, setValue] = React.useState([0, 100000]);
const handleChange = (event, newValue) => {
var val = setValue(newValue);
//I guess here is when I'm suposed to send the info to the parent
};
return (
<div className={classes.root}>
<Typography id="range-slider" gutterBottom>
Kilometers
</Typography>
<Slider
value={value}
max={500000}
min={0}
step={1000}
onChange={handleChange}
valueLabelDisplay="auto"
aria-labelledby="range-slider"
getAriaValueText={valuetext}
/>
<div id="seats-labes">
<span>0km</span>
<span>50.0000km</span>
</div>
</div>
);
}
The parent:
function WebFilter(props) {
return (
<div className="filter-web-section">
<Accordion className="filter-accordion">
<Card className="card-section">
<Card.Body>
<RangeSlider/>
</Card.Body>
</Card>
</Accordion>
</div>
)
}
export default WebFilter;
The grandfather:
class ResultModel extends Component {
render() {
return (
<div>
<h1>Texto de prueba + boton</h1> <button>+</button>
<div className="SiteHeader">
<Header/>
</div>
<div className="cars-result-content">
<div className="cars-result-content__filters">
<WebFilter
/>
</div>
<div className="car-result-content-list">
<div className="car-result-list__counter-cars">
<p>400 vehicles</p>
</div>
<div className="car-result-content-list__statBar">
<StatBar/>
</div>
<div className="cars-result-page-list__ListCars">
<ResultsView/>
</div>
</div>
</div>
</div>
)
}
}
I've been reading about declaring the hook constants at the very first component (grandfather) but I haven't been able to find a way to pass the data through the father. Thanks in advance for any hint or help.
The question is a bit short on specifics, but from what I can gather, you just need to pass down a function from component 1 through component 2 to component 3.
It's pretty straightforward actually.
In your grandpa component, create a function you want to pass:
class ResultModel extends Component {
const func1 = (data) => {console.log(data)}
render() {
...
Pass it down to father:
...
<WebFilter func1={func1} />
...
In the father component, get func1 and pass it down to child:
function WebFilter(props) {
const {func1} = props;
return (
<div className="filter-web-section">
<Accordion className="filter-accordion">
<Card className="card-section">
<Card.Body>
<RangeSlider func1={func1} />
</Card.Body>
</Card>
</Accordion>
</div>
)
}
Then in child call it like so:
export default function RangeSlider({func1}) {
const classes = useStyles();
const [value, setValue] = React.useState([0, 100000]);
const handleChange = (event, newValue) => {
var val = setValue(newValue);
//I guess here is when I'm suposed to send the info to the parent
func1("your data")
};
...
...
If you want to learn something read about react concept called lifting the state up.
Read about lifting state up in react documentation
Or just google it read one or two articles if still don't get it then post a comment I'll write full code.
I'm rendering a list of child components which contain a checkbox, and when that checkbox is clicked, I want to move that child component inside another div element.
Here's an image of what my app looks nice. I'd like to check the student names and move them up, under the "Present" sub-heading..
let ClassComp = (props) => {
const { teacher, subject, students, day } = props.classOf
const renderStudents = (students) => {
if (students && students.length > 0) {
return (
<div>
{students.map((student, index) =>
<StudentCheckbox key={index} student={student} handleCheckboxClick={handleCheckboxClick} />
)}
</div>
)
} else {
return <p style={{ margin: '10px' }} >No students registered.</p>
}
}
const handleCheckboxClick = (elId) => {
const presentStudentEl = document.getElementById('present-students')
// move StudentCheckbox element inside this element ^
}
return (
<div className="ui segment" style={segmentStyle} >
<div className="content">
<div className="ui medium header">{teacher} - {subject}</div>
<div className="ui divider"></div>
<div className="ui sub header">Students</div>
<div className="ui tiny header">Present:
<div id="present-students"></div>
</div>
<div className="ui tiny header">Absent:
<div id="absent-students">
{renderStudents(students)}
</div>
</div>
<div style={{ marginBottom: '30px' }}>
<button className="mini compact ui negative right floated button"
onClick={() => setModalVisible(true)}>Delete Class
</button>
<Link to={`/todaysclass/edit/${props.classId}`} className="mini compact ui right floated button">Edit Class</Link>
</div>
</div>
</div >
)
}
const mapStateToProps = (state, ownProps) => {
return { classOf: state.classes[ownProps.classId] }
}
export default connect(mapStateToProps, { deleteClass })(ClassComp)
and here's my child component:
const StudentCheckbox = (props) => {
const uniqId = idGenerator()
return (
<div className="field" style={{ margin: '5px' }}>
<div className="ui checkbox">
<input type="checkbox" id={uniqId} onChange={() => props.handleCheckboxClick(uniqId)} />
<label htmlFor={uniqId}>{props.student}</label>
</div>
</div>
)
}
In this case, you'll need a state for your component. Take a look in the docs:
https://reactjs.org/docs/state-and-lifecycle.html
So basically, besides props (which are "fixed"), you'll have a state, which will change when you check the items.
Your render method will use the state to place the items either in one div, or the other. So all you have to do is use setState to change the state and the render method will redraw the new one.
You're using redux to manage state. That's good. It helps properly manage/manipulate data.
In this case you're trying to decorate a view without data changes [in redux store] - that's not good, it doesn't even make sense in react.
Rendered components/view is only a [derived] View from a Model(state) - in MVC. Moving some element from one div to another in DOM (if you implement this) doesn't change the [base] state - after rerendering you'll loose these kind of changes.
UPDATE:
You should keep students' presence in the store (default false). You need a separate action (with params/payload: classId, studentId), call API (in action creator to save attendence) and reducer for change 'flag' in redux store.
Each student will have isPresent property. You can simply change your renderStudents to render both divs (additional boolean argument and apriopriate filtering at the beginning).
I want to render for drag only once, but renders the infinite loops.
i'm use The react Dnd method for this project
this warning is Show : Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
chichihandler = (id) => {
console.log('inApp', id);
this.setState({
hoverdID: 123
})
console.log("hoverd", this.state.hoverdID)
}
render() {
return (
<div className="all">
<Header />
<div className='Products_list' >
{this.state.productData.map((item) => (
<Products key={item.id} item={item} handleDrop={(productId) => this.addItem(productId)} />
))}
</div>
<div className='Store_list' >
<div className="storeName" >Store Name</div>
{this.state.storeData.map((itemS) => (
<Store key={itemS.code} itemS={itemS} chichi={(id) => this.chichihandler(id)} />
))}
</div>
</div>
)
}
storeData Code:
import React, { Component } from 'react'
import { DropTarget } from 'react-dnd'
function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
hovered: monitor.isOver(),
item: monitor.getItem()
}
}
class Store extends Component {
render() {
const { connectDropTarget, hovered, itemS } = this.props
const backcgroundColor = hovered ? 'lightgreen' : ''
if (hovered) {
this.props.chichi(itemS.name)
console.log(itemS.name)
}
return connectDropTarget(
<div>
<div id={itemS.code} className='Store' style={{ background: backcgroundColor }}>
{this.props.itemS.name}
</div>
</div>
)
}
}
export default DropTarget('item', {}, collect)(Store)
The loop occurs in the render method of your Store component, where it
calls this.props.chici(itemS.name), which
calls your chichiHandler() function, which
calls this.setState() on the parent component, which
triggers a re-render, which
causes Store to re-render, which...
It looks like you want the chichi function to be called when the user hovers over something, in which case you're better off using the onMouseOver prop on the element in question, rather than try to do it with props (see https://reactjs.org/docs/events.html#mouse-events for more info).
In general you should never call setState() from with in a render(), because it tends to cause these sorts of loops.
I am working on a mini React Redux app, where initially I am loading a list of articles. Clicking on each of these articles should render that specific articles content and details on the page.
My current app architecture is as following
App.js
class App extends Component {
render() {
return (
<div className="App">
<AppHeader></AppHeader>
<PageContent></PageContent>
</div>
);
}
}
PageContentContainer.js
const mapStateToProps = state => ({
posts: state.simpleReducer.posts
});
const mapDispatchToProps = dispatch => {
return {
frontPagePosts: () => {
dispatch(actions.frontPageItems())
}
}
};
class PageContentContainer extends Component{
componentDidMount(){
this.props.frontPagePosts();
}
render(){
return(
<div>
<main>
<FrontPageComponent posts={this.props.posts}/>
</main>
</div>
)
}
}
The FrontPageComponent is just a view rendering component with no logic in it.
class FrontPageComponent extends Component{
render(){
return(
<div>
<div className="container frontPageContainer">
{this.props.posts.map((val, idx) => {
return(
<div key={idx} className='row frontPageContent'>
<div className="col-12 col-md-12 col-sm-12 col-lg-12">
<h2>
<a href={'/' + val.slug}>
{val.title}
</a>
</h2>
</div>
<div className="col-12 col-md-12 col-sm-12 col-lg-12">{val.excerpt}</div>
</div>
)
})}
</div>
</div>
)
}
}
My current thought process is introduce another view component for viewing the individual article <ReadArticleComponent/>. Every time a user clicks on the individual article, it would change a boolean state value called showArticle in the store. Based on this it would render either components accordingly. So for example the logic in my PageContentContainer would look like
componentDidMount(){
//ajax call to the backend
if(!this.props.showArticle){
this.props.frontPagePosts();
}else{
this.props.showArticleContent()
}
}
.........
render(){
......
{!this.props.showArticle ? <FrontPageComponent.../>
: <ReadArticleComponent/>
}
}
But how would I handle the case where the user hits the back button and goes back to the page with all the articles. Is my approach right or should I be using React Router in my app?
I'm new to React and I'm puzzled on something kind of basic.
I need to append a component to the DOM after the DOM is rendered, on a click event.
My initial attempt is as follows, and it doesn't work. But it's the best thing I've thought to try. (Apologies in advance for mixing jQuery with React.)
ParentComponent = class ParentComponent extends React.Component {
constructor () {
this.addChild = this.addChild.bind(this);
}
addChild (event) {
event.preventDefault();
$("#children-pane").append(<ChildComponent/>);
}
render () {
return (
<div className="card calculator">
<p><a href="#" onClick={this.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
<ChildComponent/>
</div>
</div>
);
}
};
Hopefully it's clear what I need to do, and I hope you can help me attain an appropriate solution.
Don't use jQuery to manipulate the DOM when you're using React. React components should render a representation of what they should look like given a certain state; what DOM that translates to is taken care of by React itself.
What you want to do is store the "state which determines what gets rendered" higher up the chain, and pass it down. If you are rendering n children, that state should be "owned" by whatever contains your component. eg:
class AppComponent extends React.Component {
state = {
numChildren: 0
}
render () {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<ChildComponent key={i} number={i} />);
};
return (
<ParentComponent addChild={this.onAddChild}>
{children}
</ParentComponent>
);
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
}
const ParentComponent = props => (
<div className="card calculator">
<p><a href="#" onClick={props.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
{props.children}
</div>
</div>
);
const ChildComponent = props => <div>{"I am child " + props.number}</div>;
As #Alex McMillan mentioned, use state to dictate what should be rendered in the dom.
In the example below I have an input field and I want to add a second one when the user clicks the button, the onClick event handler calls handleAddSecondInput( ) which changes inputLinkClicked to true. I am using a ternary operator to check for the truthy state, which renders the second input field
class HealthConditions extends React.Component {
constructor(props) {
super(props);
this.state = {
inputLinkClicked: false
}
}
handleAddSecondInput() {
this.setState({
inputLinkClicked: true
})
}
render() {
return(
<main id="wrapper" className="" data-reset-cookie-tab>
<div id="content" role="main">
<div className="inner-block">
<H1Heading title="Tell us about any disabilities, illnesses or ongoing conditions"/>
<InputField label="Name of condition"
InputType="text"
InputId="id-condition"
InputName="condition"
/>
{
this.state.inputLinkClicked?
<InputField label=""
InputType="text"
InputId="id-condition2"
InputName="condition2"
/>
:
<div></div>
}
<button
type="button"
className="make-button-link"
data-add-button=""
href="#"
onClick={this.handleAddSecondInput}
>
Add a condition
</button>
<FormButton buttonLabel="Next"
handleSubmit={this.handleSubmit}
linkto={
this.state.illnessOrDisability === 'true' ?
"/404"
:
"/add-your-details"
}
/>
<BackLink backLink="/add-your-details" />
</div>
</div>
</main>
);
}
}