I've looked through the active questions that are slightly related to my question but I didn't come to a successful solution. I hope someone can help me.
I have a simple react baby code on js fiddle: http://jsfiddle.net/kb3gN/10313/
Explanation of my code can be found below the code here:
var Test = React.createClass({
getInitialState: function(){
return{
data: [
['Charlie', 10],
['Bello', 20],
['Wuffi', 15]
]
}
},
render: function(){
return(
<div>
<MakePOS data={this.state.data} />
<MakeTable />
</div>
);
}
});
var MakeTable = React.createClass({
getInitialState: function(){
return{
active: 0,
weight: 0
}
},
render: function(){
return(
<table>
<thead>
<tr>
<td>Type</td>
<td>Amount</td>
</tr>
</thead>
<tbody>
<tr>
<td>Chicken</td>
<td>some</td>
</tr>
<tr>
<td>Carotts</td>
<td>some</td>
</tr>
<tr>
<td>Apple</td>
<td>some</td>
</tr>
</tbody>
</table>
);
}
});
var MakePOS = React.createClass({
handleClick: function(){
// update state of MakeTable Component to active = id of the clicked element
// also update state of MakeTable Component to weight = data-weight value
},
render: function(){
var POS = this.props.data.map(function(i){
console.log(i[0]+' '+i[1]+' kg');
return <button onClick={this.handleClick} data-weight={i[1]} id={i[0]}>{i[0]}</button>;
});
return(<div className="testDiv">{POS}</div>);
}
});
React.render(<Test />, document.getElementById('foodApp'));
To explain my code:
the state array in the Test Component represents the users input, in this case he choose to fill in 3 pets and their weight.
Dynamically to his input the buttons are created in the MakePOS Component.
Now what I want to do is, handle the Click event on those buttons and affect the state of the MakeTable Component in Order to deal with the values hidden behind that buttons.
I want to dinamically change the table rows amounts depending on the weight of the pet.
I hope its understandable.
Thanks for your help
edit
The facebook documentation mentions something similar, but I dont really get the hint of it.
In their example they are simply calling the onClick function in the same component. I can't find any solution for child component state altering of parent components :(
http://facebook.github.io/react/tips/communicate-between-components.html
You should move your handleClick function to the component which is managing the state you want to affect, in this case Test, and then pass it down as a callback via props. Since the click is also affecting active and weight, they should be kept in the state of Test as well, so the handleClick can easily change their state.
var Test = React.createClass({
getInitialState: function(){
return{
data: [
['Charlie', 10],
['Bello', 20],
['Wuffi', 15]
],
active: 0,
weight: 0
}
},
changeWeight: function(weight){
// do whatever actions you want on click here
console.log('I am in the main Component ');
console.log(weight);
},
render: function(){
return(
<div>
<MakePOS data={this.state.data} changeWeight={this.changeWeight} />
<MakeTable active={this.state.active} weight={this.state.weight}/>
</div>
);
}
});
var MakeTable = React.createClass({
getInitialState: function(){
return{
active: 0,
weight: 0
}
},
render: function(){
return(
<table>
<thead>
<tr>
<td>Type</td>
<td>Amount</td>
</tr>
</thead>
<tbody>
<tr>
<td>Chicken</td>
<td>some</td>
</tr>
<tr>
<td>Carotts</td>
<td>some</td>
</tr>
<tr>
<td>Apple</td>
<td>some</td>
</tr>
</tbody>
</table>
);
}
});
var MakePOS = React.createClass({
handleClick: function(weight){
this.props.changeWeight(weight);
},
render: function(){
var POS = this.props.data.map(function(i){
console.log(i[0]+' '+i[1]+' kg');
return <button onClick={this.handleClick.bind(this,i[1])} key={i[0]} id={i[0]}>{i[0]}</button>;
}.bind(this));
return(<div className="testDiv">{POS}</div>);
}
});
React.render(<Test />, document.getElementById('foodApp'));
Related
I'm very new to React, and I'm trying to make a table using react that displays information about a car. Currently, I'm trying to implement two buttons on each row that would increment/decrement a certain field of the table.
This is the code for the table:
render() {
return (
<table>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Year</th>
<th>Stock</th>
<th>Price</th>
<th>Options</th>
<th></th>
</tr>
{
this.state.cars.map(car =>
<tr key={car.model}>
<td>{car.manufacturer}</td>
<td>{car.model}</td>
<td>{car.year}</td>
<td>{car.stock}</td>
<td>${car.price}</td>
<td><button type="button" onClick={this.increaseStock.bind(this)}>Increment</button></td>
<td><button type="button" onClick={this.decreaseStock.bind(this)}>Decrement</button></td>
</tr>
))
}
</table>
);
};
This so far is just fine for displaying the data and buttons, however, I'm not actually sure how to implement the buttons' functions
All the implementations for increaseStock and decreaseStock so far have done nothing on click, although I think they're being called. I'm trying to get it to either add to the stock value or subtract from it(not going below 0).
I've tried things using states, but I'm not really sure how those work.
Here's an example of how your component could be implemented. I used the functional component and a state hook because those are what I'm familiar with.
import React, { useState } from "react";
export default function CarTable() {
const [cars, setCars] = useState([
{ id: 1, name: "Car 1", stock: 5 },
{ id: 2, name: "Car 2", stock: 10 }
]);
const increase = (index) => {
const oldCar = cars[index];
// copying the car with the updated stock
const newCar = { ...oldCar, stock: oldCar.stock + 1 };
//copying the car array
const newCars = [...cars];
//replacing the old car with a new one in a new array
newCars[index] = newCar;
//updating state
setCars(newCars);
};
const decrease = (index) => {
// same as above except decreasing the stock now
const oldCar = cars[index];
const newCar = { ...oldCar, stock: oldCar.stock - 1 };
const newCars = [...cars];
newCars[index] = newCar;
setCars(newCars);
};
return (
<table>
<thead>
<tr>
<th>name</th>
<th>stock</th>
<th></th>
</tr>
</thead>
<tbody>
{cars.map((car, i) => (
<tr key={car.id}>
<td>{car.name}</td>
<td>{car.stock}</td>
<td>
<button onClick={() => increase(i)}>increase</button>
<button onClick={() => decrease(i)}>decrease</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
In short what you need to do is create a new car object with the updated stock property. Then create a new cars array with the new car object just mentioned and then set the state to this new cars array.
Here's also a CodeSandbox link where you can try it out:
https://codesandbox.io/s/charming-dijkstra-8brqz1?file=/src/CarTable.js
Hope that helps.
I'm facing a problem making a little application
for the Price Calculation of some Products in React. This is how my application looks like:
What I need now is to have a global total (sum of the partial total of the ListItem components), but i don't know how to do that with React. I tried using the same "onChange" events of the smallest component (ListItem) to trigger an event on the parent like:
handleChange:function(event){
this.props.onChange(event.target);
const target = event.target;
const name = target.name;
const value = target.value;
this.setState({
[name]: value
});
},
but in this way only this event has been triggered and didn't update the state.
Probably I'm missing something.
Recapping what I need is to pass the partial total, calculated in the ListItem, in the parent component, Table, so that I can calculate the global total.
function Header(){
return (
<h1>Calcolo costo prodotti</h1>
)
}
var ListItem = React.createClass({
getInitialState: function(){
return {name: this.props.value.product.name, costo: this.props.value.product.costo, quantita: this.props.value.product.quantita, totale: 0}
},
render: function(){
return(
<tr>
<td><input type="text" name="name" value={this.state.name} onChange={this.handleChange} placeholder="Nome..."/></td>
<td><input type="text" name="costo" value={this.state.costo} onChange={this.handleChange} placeholder="Costo unitario..."/></td>
<td><input type="text" name="quantita" value={this.state.quantita} onChange={this.handleChange} placeholder="Quantità..."/></td>
<td className="total">{this.calcoloTotale()}</td>
</tr>
)
},
handleChange:function(event){
const target = event.target;
const name = target.name;
const value = target.value;
this.setState({
[name]: value
});
},
calcoloTotale: function(){
var Ltotale = this.state.costo * this.state.quantita;
this.setState({totale: Ltotale});
return Ltotale;
}
});
var Table = React.createClass({
getInitialState: function(){
return { totale: 0 }
},
render: function(){
return(
<div>
<table>
<tr>
<th>Nome</th>
<th>Prezzo</th>
<th>Quantità</th>
<th>Totale</th>
</tr>
{this.props.items.map((prodotto) =>
<ListItem key={prodotto.id} value={prodotto}/>
)}
</table>
</div>
)
}
});
var AddNewRow = React.createClass({
render: function(){
return(
<div>
<button onClick={this.props.onClick}>+</button>
Aggiungi prodotto
</div>
)
}
});
var Calculator = React.createClass({
getInitialState: function(){
return {
counter: 2, lists: [{id: "0", product: {name: "Esempio 1",costo: "25",quantita: "3"}}, {id: "1", product: {name: "Esempio 2",costo: "32",quantita: "4"}}]
}
},
render: function(){
return (
<div className="container">
<Header />
<Table items={this.state.lists} ids={this.counter}/>
<AddNewRow onClick={this.addRow}/>
</div>
)
},
addRow: function(){
this.setState({counter: this.state.counter + 1});
var listItem = {id: this.state.counter, product:{name:"", costo: "", quantita: ""}};
var allItem = this.state.lists.concat([listItem])
this.setState({lists: allItem});
}
});
ReactDOM.render(
<Calculator />,
document.body
);
EDIT 1:
var totalVec = new Array();
function Header(){
return (
<h1>Calcolo costo prodotti</h1>
)
}
var ListItem = React.createClass({
getInitialState: function(){
return {name: this.props.value.product.name, costo: this.props.value.product.costo, quantita: this.props.value.product.quantita}
},
render: function(){
return(
<tr>
<td><input type="text" name="name" value={this.state.name} onChange={this.handleChange} placeholder="Nome..."/></td>
<td><input type="text" name="costo" value={this.state.costo} onChange={this.handleChange} placeholder="Costo unitario..."/></td>
<td><input type="text" name="quantita" value={this.state.quantita} onChange={this.handleChange} placeholder="Quantità..."/></td>
<td className="total">{this.calcoloTotale()}</td>
</tr>
)
},
handleChange:function(event){
const target = event.target;
const name = target.name;
const value = target.value;
this.setState({
[name]: value
});
this.props.updateGlobalTotal();
},
calcoloTotale: function(){
var Ltotale = this.state.costo * this.state.quantita;
totalVec[this.props.value.id] = Ltotale;
return Ltotale;
}
});
var Table = React.createClass({
getInitialState: function(){
return { totale: 0 }
},
render: function(){
return(
<div>
<table>
<tr>
<th>Nome</th>
<th>Prezzo</th>
<th>Quantità</th>
<th>Totale</th>
</tr>
{this.props.items.map((prodotto) =>
<ListItem key={prodotto.id} value={prodotto} updateGlobalTotal={this.updateGlobalTotal}/>
)}
</table>
<h1>{this.state.totale}</h1>
</div>
)
},
componentDidMount: function(){
var total = 0;
for(var i = 0; i < this.props.ids; i++){
total += totalVec[i];
}
this.setState({totale: total});
},
updateGlobalTotal: function(){
var total = 0;
for(var i = 0; i < this.props.ids; i++){
total += totalVec[i];
}
this.setState({totale: total});
}
});
var AddNewRow = React.createClass({
render: function(){
return(
<div>
<button onClick={this.props.onClick}>+</button>
Aggiungi prodotto
</div>
)
}
});
var Calculator = React.createClass({
getInitialState: function(){
return {
counter: 2, lists: [{id: "0", product: {name: "Esempio 1",costo: "25",quantita: "3"}}, {id: "1", product: {name: "Esempio 2",costo: "32",quantita: "4"}}]
}
},
render: function(){
return (
<div className="container">
<Header />
<Table items={this.state.lists} ids={this.state.counter}/>
<AddNewRow onClick={this.addRow}/>
</div>
)
},
addRow: function(){
this.setState({counter: this.state.counter + 1});
var listItem = {id: this.state.counter, product:{name:"", costo: "", quantita: ""}};
var allItem = this.state.lists.concat([listItem])
this.setState({lists: allItem});
}
});
ReactDOM.render(
<Calculator />,
document.body
);
You may want to check out Redux as Ksyqo mentioned. However, for such needs it may not be entirely what you need as it would require you to apply a wide variety of boilerplate and cognitive overhead at this particular moment when you already have existing app code written.
For smaller project(s), one might find that you can, with better results, use MobX alternative as it is a bit easier to implement especially in existing applications. It is also a lot easier to reason about. It will work pretty much out of the box, with a little bit of magic involved.
Whatever the decision is, this graph holds true for both Redux and MobX, and illustrates the problem of global state vs parent-child chained state (the former is obviously much cleaner) :
You can add a function in the parent that changes the state of that component, then send that function as a prop to the child.
var ListItem = React.createClass({
getInitialState: function(){
return {name: this.props.value.product.name, costo: this.props.value.product.costo, quantita: this.props.value.product.quantita, totale: 0}
},
...
calcoloTotale: function(){
var Ltotale = this.state.costo * this.state.quantita;
this.props.updateGlobalTotal(Ltotale);
this.setState({totale: Ltotale});
return Ltotale;
}
});
var Table = React.createClass({
getInitialState: function(){
return { totale: 0 }
},
updateGlobalTotal: function(value){
this.setState({totale: this.state.totale + value})
}
render: function(){
return(
<div>
<table>
<tr>
<th>Nome</th>
<th>Prezzo</th>
<th>Quantità</th>
<th>Totale</th>
</tr>
{this.props.items.map((prodotto) =>
<ListItem key={prodotto.id} value={prodotto} updateGlobalTotal={this.updateGlobalTotal}/>
)}
</table>
</div>
)
}
});
I think Redux is made for you.
It helps you pass properties between components that do not necessarily have a child/parent relationship. Basically, it creates a global state that you can access from anywhere.
There is a lot to say about redux (we don't call it react/redux for no reason), and this is a must-have if you want to do something powerful with React.
You can find more on the Redux documentation !
I'm getting this React.js error I'd love to know how to fix it. Is there a way to call React.cloneElement on this?
Warning: Don't set .props.children of the React component . Instead, specify the correct value when initially creating the element or use React.cloneElement to make a new element with updated props. The element was created by Seven.
Here's a full working example.
Here's my code:
var Thead = React.createClass({
displayName: 'Thead',
propTypes: function () {
return {
children: React.propTypes.node
}
},
componentWillMount: function () {
this.childTr = containsElement('tr', this.props.children)
this.props.children = reconcileChildren('th',
(this.childTr) ? this.childTr.props.children : this.props.children,
this.props.data
)
},
render: function () {
return (
<thead>
{this.childTr ? <tr {...this.childTr.props}>
{this.props.children}
</tr> : <tr>
{this.props.children}
</tr>}
</thead>
)
}
})
As #fuyushimoya said in the comments is discouraged to alter props (which I didn't know) I set the children to this.children instead.
var Thead = React.createClass({
displayName: 'Thead',
propTypes: function () {
return {
children: React.propTypes.node
}
},
componentWillMount: function () {
this.childTr = containsElement('tr', this.props.children)
this.children = reconcileChildren('th',
(this.childTr) ? this.childTr.props.children : this.props.children,
this.props.data
)
},
render: function () {
return (
<thead>
{this.childTr ? <tr {...this.childTr.props}>
{this.children}
</tr> : <tr>
{this.children}
</tr>}
</thead>
)
}
})
I am currently trying to learn react by building a simple app that works with a JSON array by calling a certain API. I would then like to show the results of the array in a table and have the ability to click one of the rows and get the data from that row to update another part of the page.
I have successfully called the API and am showing the correct data in the table but I am struggling to figure out how to show the data after the click in another part of the page. I created a click handler event and can log information to the console but cant figure out how I would show this data on the page for the relevant row that is clicked.
So I currently have this in my page:
<div class="container"></div>
<script type="text/jsx">
var ShowData = React.createClass({
getInitialState: function() {
return { data: [] };
},
componentDidMount: function() {
$.get(this.props.source, function(data) {
if (this.isMounted()) {
this.setState({
data: data
});
}
}.bind(this));
},
handleClick: function(i) {
console.log('You clicked: ' + this.state.data[i].event + this.state.data[i].name);
},
render: function() {
var self = this;
return (
<table className="m-table">
<thead>
<tr>
<th>Event</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{this.state.data.map(function(type, i){
return (
<tr>
<td title="Type" onClick={self.handleClick.bind(this, i)} key={i}>{type.event}</td>
<td title="Type">{type.name}</td>
</tr>
)
})}
</tbody>
</table>
);
}
});
React.render(
<ShowData source="url of api" />,
document.getElementById('container')
);
</script>
Below this script is another container that I would like to show the results when the table row is clicked.
So to summarize, I want to display the data from the API call in a table, upon clicking on one of the table rows I want to take the table row data and format it in another container on the page.
I'd move both the table-component and the display area-component into a parent component that has the responsibility of managing state. The parent component will pass a callback for handling row clicks down to the table, something along the lines of the below edits to your code example. (Heads-up: haven't slept in the past 40+ hours when writing this, due to travel..)
<div class="container"></div>
<script type="text/jsx">
var TableComponent = React.createClass({
handleClick: function(i) {
this.props.clickHandler(i);
},
render: function() {
var self = this;
return (
<table className="m-table">
<thead>
<tr>
<th>Event</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{this.props.data.map(function(type, i){
return (
<tr>
<td title="Type" onClick={self.handleClick.bind(this, i)} key={i}>{type.event}</td>
<td title="Type">{type.name}</td>
</tr>
)
})}
</tbody>
</table>
);
}
});
var DetailsArea = React.createClass({
render: function() {
var rowDetails = this.props.rowDetails;
return <div>{rowDetails.name}</div>;
}
});
var ParentComponent = React.createClass({
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
$.get(this.props.source, function(data) {
if (this.isMounted()) {
this.setState({
data: data
});
}
}.bind(this));
},
rowClickHandler: function(rowIndex) {
this.setState({selectedRow: rowIndex});
},
render: function() {
var tableData = this.state.data;
return (
<TableComponent data={tableData} clickHandler={rowClickHandler} />
<DetailsArea rowDetails={tableData[this.state.selectedRow]} />
}
});
React.render(
<ParentComponent source="url of api" />,
document.getElementById('container')
);
</script>
Based on https://facebook.github.io/react/docs/two-way-binding-helpers.html , I am trying to make a multi-checkbox-status todo that displays, at start, a textarea. I am trying to follow the model, and where it has:
var WithLink = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function() {
return <input type="text" valueLink={this.linkState('message')} />;
}
});
I have:
var Todo = React.createClass(
{
mixins: [React.addons.LinkedStateMixin],
getInitialState: function()
{
var that = this;
return {
children: [],
description: '',
id: 0
};
},
and, in the same class:
render: function()
{
var that = this;
return (
<table>
<tbody>
{that.state.children}
</tbody>
<tfoot>
<td>
<textarea className="description"
placeholder=" Your next task..."
onChange={that.onChange} name="description"
valueLink={that.linkState('description')}
id="description"></textarea><br />
<button onClick={that.handleClick}
id="save-todo">Save</button>
</td>
</tfoot>
</table>
);
}
When it renders, there's nothing but an h1 and a jQuery bgstretcher on the page. Chrome's Inspect Element doesn't turn up what I expected and had earlier, a TEXTAREA and a BUTTON inside a table. The log doesn't display any either errors or messages.
At some point I expect further work so that a description is handed off to a new child and re-initialized, but I'm not there yet.
Suggestions for what I should do for it to display?