Change state of a parent from the child in ReactJs - reactjs

Supposing I have this parent component.
Parent.jsx
render() {
const headers = ["id","desc1", "desc2", "actions"];
return(
<div>
<input type = "text" placeholder = "Product Brand" value={ this.state.desc }/>
<input type = "text" placeholder = "Product Brand" value={ this.state.desc2 }/>
<button type = "button" onClick = { this.handleSubmit.bind(this) }>Add</button>
<CustomTable mystate = { this.state } header = { headers } data = { this.props.store.productbrandlist }/>
</div>
)
}
and this CustomTable.jsx
renderHeaders(){
return(
this.props.header.map(function(header, index){
return <th key={index}>{header}</th>
})
)
}
renderRows(){
// console.log("here1");
// return(
// <ListItem datarows = { this.props.data }/>
// )
return this.props.data.map((list, id) => <ListItem mystate = {this.props.mystate} key_id={id} key={id} col = {list}/> )
}
render(){
return(
<table className="table">
<thead>
<tr>{ this.renderHeaders() }</tr>
</thead>
<tbody>
{ this.renderRows() }
</tbody>
</table>
)
}
and this component which render the rows of the table
render(){
return(
<tr key = { this.props.key_id }>
{ this.renderColumns() }
{ this.renderActions() }
</tr>
)
}
renderColumns(){
var columns = []
for (var key in this.props.col)
{
if (this.state.isEditing){
columns.push(<td key = {key.id}><input ref = "txt" type = "text" value = { this.state.itemValue } onChange = { this.onTextChange.bind(this) } /></td>)
}
else
{
columns.push(<td key = {key.id}>{ this.props.col[key] }</td>)
// this.setState({
// itemValue: key,
// isEditing: false
// })
}
}
return columns
}
renderActions(){
if (this.state.isEditing){
return (
<td>
<button type="button" onClick = { this.handleSaveClick.bind(this) }>Save</button>
<button type="button" onClick = { this.handlCancelClick.bind(this) }>Cancel</button>
</td>
)
}
return(
<td>
<button type="button" onClick = { this.handleEditClick.bind(this) }>Edit</button>
<button type="button" onClick = { this.handleDeleteClick.bind(this) }>Delete</button>
</td>
)
}
My question is how do I configure it in such a way that when I click on the button edit which is created in the ListItem Component. The data will be displayed in the inputbox which is created in the parent.jsx

Looking at your code, you just simply need to pass a reference to a parent method to the desired child, through props. I don't have your full code, so this is not tested, but should give you an idea on how to do it.
If there was another child layer between your parent and the ListItem, I would definitely encourage using Redux. Personally I'm ok with passing references two levels deep using props for simple projects.
To get your ListItem values to show up in the parent input fields, make the following changes:
In your Parent:
// you need two-way data binding between state and input values
onChange(){
this.setState({
desc: this.refs.desc1.value,
desc2: this.refs.desc2.value
});
}
// this method will get triggered by ListItem's edit button onClick
onRowEdit(desc1, desc2){
this.setState({
desc: desc1,
desc2: desc2
});
}
render() {
const headers = ["id","desc1", "desc2", "actions"];
return(
<div>
<input ref="desc1" type = "text" placeholder = "Product Brand" value={ this.state.desc } onChange={this.onChange.bind(this)} />
<input ref="desc2" type = "text" placeholder = "Product Brand" value={ this.state.desc2 } onChange={this.onChange.bind(this)/>
<button type = "button" onClick = { this.handleSubmit.bind(this) }>Add</button>
<CustomTable onEdit={ this.onRowEdit } mystate = { this.state } header = { headers } data = { this.props.store.productbrandlist }/>
</div>
)
}
Your custom table renderRows:
renderRows(){
// console.log("here1");
// return(
// <ListItem datarows = { this.props.data }/>
// )
return this.props.data.map((list, id) => <ListItem onEdit={this.props.onEdit} mystate = {this.props.mystate} key_id={id} key={id} col = {list}/> )
}
Finally in your ListItem inside handleEditClick method call the function passed in the props:
handleEditClick(){
// const desc1 = ...
// const desc2 = ...
this.props.onEdit(desc1, desc2); // this call will cause desc1, desc2 values to show up in the parent's input fields.
}

Related

React: how to select all checkboxes of columns/rows in children component by clicking on parent div

I have a React components to generate a filter panel.
This is how the part of it looks like:
What I want to achieve is:
When user click on "USAGE" then all checkboxes will be selected/unselected (it depends on which options there are more)
When user click on subtype header (receiver/sender) then all checkboxes in one column will be selected/unselected
When user click on the label then checkboxes in one row will be selected/unselected
Now time for my code, 1st is header div (in screenshot it is "USAGE" label):
render(): React.ReactNode {
return (
<div className={'col-xs-12 m-b-xs filter-header'}>
<div className={'inline'} onClick={this.props.options.toggle}>
{this.title}
{this.prepareMissingSystemInformation()}
</div>
{this.collapseLink}
</div>
);
}
Then options are above:
export interface QuickFilterOptionsProps {
options: any;
isCollapsed: boolean;
}
export class QuickFilterOptions extends React.PureComponent <QuickFilterOptionsProps, {}> {
updateQuickFilters() {
//method to update filters after changing checkboxes
}
render(): React.ReactNode {
const newProps = {
...this.props,
updateFilters: this.updateQuickFilters
}
return (
<>
<div className={'col-xs-12'} id={'QuickFilterComponent' + this.props.options.filter.label}
style={{display: this.props.isCollapsed ? 'none' : 'block'}}>
<table>
<OptionValues {...newProps}/>
</table>
</div>
</>
);
}
}
And option values with checkboxes:
const GenerateValue = (props: any) => {
// For each subtype
const array = Object.values(props.options.filter.options).slice(0, 10).map((option, index) => {
const label = option['id'] === '' ? FilterPrettyPrinter.getTranslationForEmptyValue(props.options.filter.property) : option['id'];
const optionLabel = (
<td className={'clickable option-label'} onClick={props.options.toggle}
onMouseEnter={props.options.hoverIn}
onMouseLeave={props.options.hoverOut}>{label}</td>);
const checkboxes = props.options.filter.subtypes.map((subtype) => {
// Checkbox is disabled when it relates to a property that has no connections or calls
const disabled = option['properties'][subtype.property] === undefined; // || option.properties[subtype.property].calls === 0;
// Checkbox is selected when it is disabled or has a selected property set to true
const [selected, setSelected] = useState(disabled || option['properties'][subtype.property].selected);
const classNames: string[] = ['ff-checkbox-square'];
// Check if needed
if (selected) {
classNames.push('selected');
}
const optionValue = () => {
if (selected) {
option['properties'][subtype.property].selected = true;
props.updateFilters();
props.options.onCheckboxClick();
} else {
option['properties'][subtype.property].selected = false;
props.updateFilters();
props.options.onCheckboxClick();
}
};
const toggleCheckbox = () => {
setSelected(!selected);
}
// TOOLTIP
return (
<td key={UUID.UUID()}>
<div className={'ff-checkbox>'}>
<div
className={'ff-checkbox-square ' + (selected ? 'checked ' : '') + (disabled ? 'disabled ' : '')}>
<input type='checkbox'
className={classNames.join(' ')}
defaultChecked={selected}
disabled={disabled}
onClick={() => toggleCheckbox()}
onChange={() => optionValue()}/>
</div>
</div>
</td>
);
});
return (
<tr key={UUID.UUID()}>
{checkboxes}
{optionLabel}
</tr>);
});
return <>
{array}
</>;
};
export function OptionValues(props) {
const isShowMoreButton = Object.keys(props.options.filter.options).length - props.options.maxFilterOptions > 0;
return (
<>
{props.options.filter.subtypes.length > 1 ? OptionSubtypeHeader(props.options) : null}
<tbody>
<GenerateValue {...props}/>
</tbody>
{isShowMoreButton ? QuickFilterFooter(props.options) : null}
</>);
Can someone help me with this?

Is there something wrong with my react parent class?

I am practicing using react to build a simple table. Here my table has three columns. (name, job, delete). There is already some data in the table. In the third column, I want to build a button so the user can click and cancel the whole row
I already fixed several bugs but the table still does not show up
const TableBody = props => {
const rows = props.fillTheData.map((row, index) => {
return (
<tr key={index}>
<td>{row.name}</td>
<td>{row.job}</td>
<td><button onClick={() => props.removeCharacter(index)}>Delete</button></td>
</tr>
);
});
return <tbody>{rows}</tbody>;
}
class App extends React.Component {
state = {
character : [ ]
};
removeCharacter = index => {
const {character} = this.state;
this.setState({
character: character.filter((element, i) => {
return i !== index;
})
});
}
handleSubmit = character => {
this.setState({character:[...this.state.character,character]})
}
render() {
return(
<div class= "container">
<Table characters = {this.state.character} removeCharacter = {this.removeCharacter} />
<Form handleSubmit = {this.handleSubmit}/>
</div>
)
}
}
class Form extends React.Component {
constructor (props) {
super( props );
this.initialState = {
name: '',
job: ''
};
this.state = this.initialState;
}
handleChange = event => {
const{name,job} = event.target;
this.setState(
{
[name]: value
}
);
}
submitForm = () => {
this.props.handleSubmit(this.state);
this.setState(this.initialState);
}
render() {
const { name, job } = this.state;
return (
<div class="container2">
<form>
<label>Name</label>
<input
type="text"
name="name"
value={name}
onChange={this.handleChange} />
<label>Job</label>
<input
type="text"
name="job"
value={job}
onChange={this.handleChange}/>
</form>
<input
type="button"
value="Submit"
onClick={this.submitForm} />
</div>
);
}
}
export default Form;
class Table extends React.Component {
render(){
const {characters, removeCharacter} = this.props;
return(
<table>
<TableHeader />
<TableBody fillTheData = {characters} removeCharacter= {removeCharacter} />
</table>
)
}
}
const TableHeader = () => {
return (
<thead>
<tr>
<th>Name</th>
<th>Job</th>
<th>Delete</th>
</tr>
</thead>
);
}
Right now, we have a cool Table component, but the data is being hard-coded. One of the big deals about React is how it handles data, and it does so with properties, referred to as props, and with state. First, we’ll focus on handling data with props.
Then let’s move all that data to an array of objects, as if we were bringing in a JSON-based API. We’ll have to create this array inside our render().
The handleChange method of Form component is declaring a constant named job which doesn't exist in event.target object and it is setting the state with value variable which doesn't exists in that scope.
So change
const{name,job} = event.target;
to
const{name,value} = event.target;
your code will just work fine.

React Checkbox Group - Set initial state from API

I have a CheckboxGroup component which takes in an options array prop and generates CheckboxInput components. On page load I make a call to an API which returns an array of pre-selected checkboxes (delivered to the value prop). Depending on the logged in user, this call can return an empty array or a selection of previously selected checkbox options.
The following code successfully takes the response of the API call and sets the relevant checkboxes to 'checked'. The issue I have is that this code doesn't allow me to make changes to the checkboxes after page load (clicking a checkboxes has no effect).
I think there is also some disconnect between the initial selectedCheckboxes state and the value of the API call but I read that setting props as initial state is an anti-pattern (e.g. selectedCheckboxes: props.value,).
export default class CheckboxGroup extends Component {
constructor(props) {
super(props);
this.state = {
selectedCheckboxes: [],
};
}
addCheckboxToSelected = (id) => {
if (this.state.selectedCheckboxes.includes(id)) {
// Remove checkbox from array and update state
const filteredArray = this.state.selectedCheckboxes.filter(item => item !== id);
this.setState({ selectedCheckboxes: filteredArray });
} else {
// Add checkbox to array and update state
this.setState({ selectedCheckboxes: this.state.selectedCheckboxes.concat(id) });
}
}
checkIfSelected = (checkboxValue) => {
const preSelectedCheckboxes = this.props.value;
let selectedBool = false;
preSelectedCheckboxes.some(function(object) {
if (object.id === checkboxValue) {
selectedBool = true;
}
return false;
});
return selectedBool;
}
render() {
const { label, name, options } = this.props;
return (
<div className="form-group form-inline">
<span className="checkboxgroup-heading">{label}</span>
<div className="form-group-container">
{options.map(object => (
<CheckboxInput
key={object.value}
name={name}
label={object.label}
onChange={this.addCheckboxToSelected}
value={object.value}
checked={this.checkIfSelected(object.value)}
/>
))}
</div>
</div>
);
}
}
This is the stateless CheckboxInput component
const CheckboxInput = ({ name, label, onChange, value, checked }) => {
return (
<div className="field form-group filter-input">
<input
type="checkbox"
id={value}
name={name}
value={value}
onChange={() => onChange(value)}
checked={checked}
/>
<label htmlFor={value} className="form-control">{label}</label>
</div>
);
};
Check the following code snippet. This might help. Let me know if you have questions.
const CheckboxField = ({checked, onChange}) => {
return (
<input type="checkbox" checked={checked} onChange={ev => onChange(ev.target.checked)} />
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
options: [{id: "1", checked: true}, {id: "2", checked: false}]
};
}
handleCheckboxChange(checked, option) {
const {options} = this.state;
var cOptions = [...options];
for(var i in cOptions) {
if(cOptions[i].id == option.id) {
cOptions[i].checked = checked;
}
}
this.setState({
options: cOptions
}, () => console.log(options));
}
render() {
const {options} = this.state;
return (
<div>
{
options.map(option => {
return (
<CheckboxField key={option.id} checked={option.checked} onChange={value => this.handleCheckboxChange(value, option)} />
)
})
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Editable Form ReactJS

import React, { Component } from 'react';
let _ = require('lodash');
import {bindActionCreators} from "redux";
import {connect} from 'react-redux';
import {fetchedBeaconsEdit} from '../../actions/';
import {editBeacon} from '../../actions/index';
// TODO - come up with a decent name
class InfoRow extends Component {
render() {
return (
<tr>
<td>
{ this.props.beacon === "name"
|| this.props.beacon === "major"
|| this.props.beacon === "minor"
|| this.props.beacon === "beaconType" ?
<span>{this.props.beacon}<span className="font-css top">*</span></span>:
<span>{this.props.beacon}</span>
}
</td>
<td>
{ this.props.beacon !== "beaconType" &&
this.props.beacon !== "uuid" &&
this.props.beacon !== "status" &&
this.props.beacon !== "store"&&
this.props.beacon !== "group" ?
<div>
<input type="text"
className="form-control"
defaultValue={this.props.beaconValue}
name={this.props.beaconValue}
onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}
/></div>:
this.props.beacon === "uuid" && this.props.beacon === "status" && this.props.beacon=== "store"?
<span></span>:
this.props.beacon === "beaconType"?
<select defaultValue={this.props.beaconValue} name={this.props.beaconValue} className="form-control" onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}>
<option name="ibeacon">IBEACON</option>
<option name="eddystone">EDDYSTONE</option>
</select>:this.props.beaconValue
}
</td>
</tr>
)
}
}
class BeaconEdit extends Component {
constructor(props){
super(props);
this.state = {
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleInputChange(beacon, value) {
this.setState({
[beacon]: value
});
}
handleClick = () =>{
Object.keys(this.props.ebcn).map((key)=> {
if (this.state[key] !== undefined) {
this.props.editBeaconGroup[key]=this.state[key];
}
})
this.props.handleSubmitProp(this.props.editBeaconGroup);
}
render() {
const rows = [];
let a = this.props.ebcn;
Object.keys(this.props.ebcn).map((keyName, keyIndex) =>{
if (keyName === "store" || keyName === "group") {
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].name.toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
}else{
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
}
});
return (
<div className="col-md-6">
<div className="">
<table className="table table-clear">
<tbody>
{rows}
</tbody>
</table>
</div>
<div className="px-1" >
<button className="btn btn-sm btn-info btn-size btn-block" onClick={this.handleClick}>Save</button>
</div>
</div>
)
}
}
class BeaconDetailEditComponent extends Component {
constructor(props){
super(props);
this.state = {
editbeacons: {}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.validateName = this.validateName.bind(this);
this.validateMajor = this.validateMajor.bind(this);
this.validateMinor = this.validateMinor.bind(this);
}
validateMinor = (minor) => {
var re = /^[0-9]+$/;
return re.test(minor);
}
validateMajor = (major) => {
var re = /^[0-9]+$/;
return re.test(major);
}
validateName = (name) => {
var re = /^[A-Za-z ]+$/;
return re.test(name);
};
handleSubmit (beaconedited) {
console.log(beaconedited.name);
if (!this.validateName(beaconedited.name)) {
alert('Name can not be an integer')
}
else if (!this.validateMajor(beaconedited.major)) {
alert('Major number can only be an integer')
}
else if (beaconedited.major.length > 5) {
alert('Major number can not exceed 5 digits')
}
else if (!this.validateMinor(beaconedited.minor)) {
alert('Minor number can only be an integer')
}
else if (beaconedited.major > 65535) {
alert('Major number can not exceed the limit of 65535')
}
else if (beaconedited.minor > 65535) {
alert('Minor number can not exceed the limit of 65535')
}
else {
this.props.editBeacon(beaconedited, this.props.location.query.id);
}
}
componentWillMount = () => {
this.props.fetchedBeaconsEdit(this.props.location.query.id);
};
render() {
return (
<div className="container px-3 mr-3">
<div>
<div className="col-md-6 col-md-offset-3"><h1>Edit Beacon Information</h1></div>
</div>
<br/>
<br/>
{ this.props.ebcn != null?
<div>
<BeaconEdit ebcn={this.props.ebcn} handleSubmitProp={this.handleSubmit} editBeaconGroup={this.state.editbeacons}/>
</div> :
<center><img className="gif-size" src={'img/avatars/default.gif'} alt="Loading"/></center>
}
</div>
)
}
}
function mapStateToProps(state) {
return {
eBeacon: state.eBeacon,
ebcn: state.beacons
}
}
function matchDispatchToProps(dispatch){
return bindActionCreators({editBeacon: editBeacon, fetchedBeaconsEdit: fetchedBeaconsEdit}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(BeaconDetailEditComponent);
i had provided the code snippet
what i am doing is
i had fetched the values from the server and were shown in the fields and I'm making this page as editable form
what i want to do is now to take the values changed or changed by the user and to print them in console.
i had applied handleInputChange and its showing changed values while changing but i want to see those values in console on button click as well
how to do it?
First, the manner your are changing the state is not good. You are setting the new state to have only a single attribute which is the name and value of the field which is modified.
Coming to how to print the value of the field you are modifying, you should do a console log in the handle change method.
That said, your code should look like:
handleInputChange(event) {
//const {name, value} = event.target;
//const oldState = this.state;
//const newState = Object.assign({},oldState,{[name]:value});
//this.setState(newState);
this.setState({
[event.target.name]: event.target.value
});
//printing the input name and value as it is being modified.
console.log(name + ":" + value);
//if you want to see the value of the current state uncomment the line below
//console.log("State=" + JSON.stringify(newState));
}
printState = () => {
console.log("State="+JSON.stringify(this.state));
}
For more insight on how to modify objects with assign method see: MDN doc on Object.assign()
If you want to print the current state on clicking the save button then add the onClick attribute to the button as shown in the code below:
<button value="Save" onClick={this.printState}/>
Change InfoRow to
class InfoRow extends Component {
render() {
return (
<tr>
<td>
{this.props.beacon}
</td>
<td>
<input type="text"
className="form-control"
defaultValue={this.props.beaconValue}
value={this.props.name}
name={this.props.beaconValue}
onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}
/>
</td>
</tr>
)
}
}
and render to
Object.keys(this.props.ebcn).map((keyName, keyIndex) =>{
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
});
and
handleInputChange(beacon, value) {
this.setState({
[beacon]: value
});
}
and handleClick to
handleClick = () =>{
Object.keys(this.props.ebcn).map((key)=> {
console.log(this.state[key]);
}))
}

React - require some aid with saving data to a table

I have a table with dynamically generated column headers and rows, a user can enter in data to the rows and when they click save the data should be saved into that row but at the moment it isn't saving the values to the table but it is saving them on my database. Ideally I would like it so that when the save button is clicked the data will be saved to the row and is then viewable in that row (if that makes any sense).
Here is the code I am working with (I know it is a mess at the moment!):
Code for data input form:
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class RowForm extends React.Component {
state = {dataEntries: []};
onChange = (event, element) => {
let dataEntries = this.state.dataEntries;
dataEntries[element] = event.target.value;
this.setState({dataEntries});
};
editStop = () => {
this.props.editStop();
};
handleSubmit = (e) => {
e.preventDefault();
let access_token = AppStore.getToken();
let id = AppStore.getTable().id;
let dataEntries = this.state.dataEntries;
let dataEntriesArray = [];
for (let key in dataEntries) {
if (dataEntries.hasOwnProperty(key)) {
dataEntriesArray.push({contents: dataEntries[key]});
}
}
this.props.handleSubmit(access_token, id, dataEntriesArray);
};
componentDidMount() {
let nameArray = AppStore.getTable().columns.map((obj) => {
return obj.name;
});
let dataEntries = nameArray.reduce((obj, name) => {
obj[name] = null;
return obj;
}, {});
this.setState({dataEntries});
}
render() {
let {dataEntries} = this.state;
return (
<tr>
{Object.keys(dataEntries).map((element) => {
return (
<td key={element}><input type="text" className="form-control" id={element} placeholder="enter data" value={dataEntries[element]} onChange={event => this.onChange(event, element)} /></td>
);
})}
<td>
<button className="btn btn-default" onClick={this.editStop}><i className="fa fa-ban"></i>Cancel</button>
<button className="btn btn-success" onClick={this.handleSubmit}><i className="fa fa-check"></i>Save</button>
</td>
</tr>
);
}
After the data has been entered and submitted (it is an array of objects like dataEntriesArray = [{contents: "value"}, {contents: "value"}, {contents: "value"}, {contents: "value"}].
And here is how I am rendering the table (this is where the problem is I think):
import React from 'react';
import TableHeader from './TableHeader.jsx';
import RowForm from './RowForm.jsx';
import {createRow} from '../../actions/DALIActions';
import AppStore from '../../stores/AppStore';
export default class Table extends React.Component {
state = {rows: [], isNew: false, isEditing: false};
handleAddRowClickEvent = () => {
let rows = this.state.rows;
rows.push({isNew: true});
this.setState({rows: rows, isEditing: false});
};
handleEdit = (row) => {
this.setState({isEditing: true});
};
editStop = () => {
this.setState({isEditing: false});
};
handleSubmit = (access_token, id, dataEntriesArray) => {
createRow(access_token, id, dataEntriesArray);
};
render() {
let {rows, isNew, isEditing} = this.state;
let headerArray = AppStore.getTable().columns;
return (
<div>
<div className="row" id="table-row">
<table className="table table-striped">
<thead>
<TableHeader />
</thead>
<tbody>
{rows.map((row, index) => this.state.isEditing ?
<RowForm formKey={index} key={index} editStop={this.editStop} handleSubmit={this.handleSubmit} /> :
<tr key={index}>
{headerArray.map((element, index) => {
return (
<td key={index} id={element.id}></td>
);
})}
<td>
<button className="btn btn-primary" onClick={this.handleEdit.bind(this, row)}><i className="fa fa-pencil"></i>Edit</button>
</td>
</tr>)}
</tbody>
</table>
</div>
<div className="row">
<div className="col-xs-12 de-button">
<button type="button" className="btn btn-success" onClick={this.handleAddRowClickEvent}>Add Row</button>
</div>
</div>
</div>
);
}
}
I am using flux at the moment, and would ideally like to keep using it for now (I know about redux but I would ideally like to get it working in flux before I start refactoring my code). I suspect it is a problem with the way I am rendering my table.
Any help would be much appreciated, especially examples!
Thanks for you time!
It looks like you probably want to extract your table data into your store, your UI child elements trigger change events, your store then updates its data and trigger change events which your parent component can listen for and update.
Something like the following simplified example, which mutates array elements:
class Store extends EventEmitter {
constructor() {
super()
this.data = [ 'a', 'b', 'c' ]
}
onChange() {
this.emit( 'update', this.data )
}
mutate( index, value ) {
this.data[ index ] = value
this.onChange()
}
}
var store = new Store()
class ChildComponent extends React.Component {
constructor( props ) {
super( props )
}
// You probably want to use a dispatcher rather than directly accessing the store
onClick = event => {
store.mutate( this.props.index, this.props.value + 'Z' )
}
render() {
return <button onClick={ this.onClick }>{ this.props.value }</button>
}
}
class ParentComponent extends React.Component {
constructor( props ) {
super( props )
// You probably want to be smarter about initially populating state
this.state = {
data: store.data
}
}
componentWillMount() {
store.on( 'update', data => this.setState({ data: data }) )
}
render() {
let cells = this.state.data.map( ( value, index ) => <ChildComponent index={ index } value={ value } /> )
return (
<div>
{ cells }
</div>
)
}
}
For brevity child components here directly tell the store to change values, you'd probably want to dispatch messages/actions and have the store decide how to respond, the key is simply that the store data is passed to the parent component, which updates its state and triggers a re-render.
The flow here is that essentially the UI is dumb, it simply renders the data that it gathers from the store and dispatches a message to tell the store to update/mutate when a user action is detected (in this case a button press but it sound like you'll need an input of some sort), when the data in the store changes it emits (or could use a dispatcher also) a change event which forces the UI to re-render the new state. As child components are re-rendered at this stage they are populated with the new data state, ensuring that your UI remains consistent.

Resources