Accessing "More Details" when clicked in a React Table - javascript

I am still learning React. I have an array of fake data in a json file that I am using to populate a table that I made with react-table. I have the table as a component. I have added a "Details" button in a column on the table, and when it's clicked, I want a second component to appear on the screen below the table that contains additional details that I could not reasonable fit onto the table. I assume I need to do some sort of handleClick function that's called with the button's onClick, and use that handleClick to setState some information that will let access it through the Details component. On the table component, I did a forEach to assign everything it's own ID number in efforts to use that to somehow call upon that specific item for more details, but I'm not really sure how to utilize that.
App.js
import './App.css';
import Details from './components/Details'
import Table from './components/Table';
import React, { Component } from 'react';
class App extends Component {
state = {
}
handleClick(){
this.setState({
})
}
render() {
return (
<>
<Table />
<Details />
</>
)}
}
export default App;
Table.js
import React, { useMemo } from "react";
import Data from "../sample.json"
import { useTable, useSortBy, usePagination } from 'react-table'
import { Columns } from './Columns'
import './Table.css'
function Table() {
const columns = useMemo(() => Columns, [])
const data = useMemo(() => Data, [])
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state,
prepareRow,
} = useTable(
{columns, data, initialState: {pageSize: 5}},
useSortBy,
usePagination)
const { pageIndex } = state
Data.forEach((item, i) => {
item.id = i + 1
})
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
)})}
</tr>
)})}
</tbody>
</table>
<div>
<center>
<span>
Page{' '}{pageIndex + 1} of {pageOptions.length}{' '}
</span>
<button
onClick={() => previousPage()}
disabled={!canPreviousPage}>
Previous
</button>
<button
onClick={() => nextPage()}
disabled={!canNextPage}>
Next
</button>
</center>
</div>
</>
)
}
export default Table;
Columns.js
export const Columns = [
{
Header: 'ID',
accessor: 'id'
},
{
Header: 'Payee Name',
accessor: 'Payee.Name'
},
{
Header: 'Payee Fax',
accessor: 'Payee.Fax'
},
{
Header: 'Payee Phone',
accessor: 'Payee.Phone'
},
{
Header: 'Link',
accessor: '',
Cell: props => <button onClick={() => {}}>Details</button>
},
]
Screenshot below is more or less what I want to see. Just a table, and the expanded details for the last item clicked on that table. Right now, I simply have some code mapping over all the data and giving them their own cards. But I just want one at a time, ideally. Current code mapping everything will be below.
enter image description here
import React from 'react';
import Data from '../sample.json'
function Details() {
return (
<>
{ Data.map(item => {
return(
<div className="item">
<div className="itemRow1">
<div>
<h4>{ item.Payee.Name }</h4>
<div>{ item.Payee.Phone }</div>
<div>{ item.Payee.Address.Address1 }</div>
<div>{ item.Payee.Address.Address2 }</div>
<div>{ item.Payee.Address.City }</div>
<div>{ item.Payee.Address.StateOrProvince }</div>
<div>{ item.Payee.Address.Country }</div>
<div>{ item.Payee.Address.PostalCode }</div>
<br/>
<div>{ item.Payee.Attention} </div>
<div>{ item.Payee.SubmissionDate }</div>
</div>
<div>
<h4>Payment Information</h4>
<div>{ item.Payment.PAN }</div>
<div>{ item.Payment.CVV }</div>
<div>{ item.Payment.Exp }</div>
</div>
<div>
<h4>Remittance</h4>
<div>{ item.Remittance.map(payor => {
return(
<div className="payor">
<div>{ payor.PayorName }</div>
<div>{ payor.PayorId }</div>
<div>{ payor.InvoiceNo }</div>
<div>{ payor.Amount }</div>
<br/>
</div>
)})}
</div>
</div>
</div>
<div>
{item.Remittance.map(payor => {
return (
<div className="description">
{payor.Description}
</div>
)})}
</div>
</div>
)})}
</>
);
}
export default Details;

Related

How to delete an item from a list in react

In the List.js file im trying to remove each item from the list
Currently within App.js i have a button within the section that is able to delete all birthdays by passing in a empty array.
**App.js**
import React, { useState } from "react";
import data from "./data";
import List from "./List";
function App() {
const [people, setPeople] = useState(data);
return (
<main>
<section className="container">
<h3>{people.length} birthdays today!</h3>
<List people={people} />
<button onClick={() => setPeople([])}> Clear Birthdays </button>
</section>
</main>
);
}
export default App;
**List.js**
import React from "react";
const List = ({ people }) => {
return (
<>
{people.map((person) => {
const { id, name, age, image } = person;
return (
<article key={id} className="person">
<img src={image} alt={name} />
<div>
<h4>{name}</h4>
<p>{age}</p>
</div>
</article>
);
})}
</>
);
};
export default List;
I tried creating a new function and button with an arrow function in the List.js that passes in id like so
const removeItem = (id) => {
let newPeople = people.filter((person) => person.id !== id);
setPeople(newPeople);
};
<button onClick={() => removeItem(id}>Clear</button>
refactored code working solution below
*** App.Js ***
import React, { useState } from "react";
import data from "./data";
import List from "./List";
function App() {
const [people, setPeople] = useState(data);
const removeItem = (id) => {
let newPeople = people.filter((person) => person.id !== id);
setPeople(newPeople);
};
return (
<main>
<section className="container">
<h3>{people.length} birthdays today!</h3>
<List people={people} removeItem={removeItem} />
<button onClick={() => setPeople([])}> Clear Birthdays </button>
</section>
</main>
);
}
export default App;
*** List.js ***
import React from "react";
const List = ({ people, removeItem }) => {
return (
<>
{people.map((person) => {
const { id, name, age, image } = person;
return (
<article key={id} className="person">
<img src={image} alt={name} />
<div>
<h4>{name}</h4>
<p>{age}</p>
</div>
<button onClick={() => removeItem(id)}>Clear</button>
</article>
);
})}
</>
);
};
export default List;
request for comments for different solutions.
Just pass your removeItem function to the List component:
return (
<main>
<section className="container">
<h3>{people.length} birthdays today!</h3>
<List people={people} removeItem={removeItem} />
<button onClick={() => setPeople([])}> Clear Birthdays </button>
</section>
</main>
);
And call it when the button is clicked and pass the person's id to the function

Pass a component to another inside a props array

How to pass a relative component through props to another component to render it?
For example pass to component table from app.js a props with a component button with relative calls and just call it like in code bellow? Is it possible?
Error
Element type is invalid: expected a string (for built-in components) or a >class/function (for composite components) but got: object.
In the Table.js component
import React from "react";
import { Container } from "./styles";
const Table = ({ data, columns }) => {
return (
<Container>
<thead>
<tr>
{columns.map((header) => {
return <th>{header.text}</th>;
})}
</tr>
</thead>
<tbody>
{data.map((dado) => {
return (
<tr>
{columns.map((column) => {
var { Mask } = column;
console.log(Mask);
return (
<td>
<Mask />
</td>
);
})}
</tr>
);
})}
</tbody>
</Container>
);
};
export default Table;
App.js
import React from "react";
import "./styles.css";
import Table from "./Table";
export default function App() {
const data = [
{ name: "teste", age: 30, place: "brazil", place2: "brazil" },
{ name: "teste", age: 30, place: "brazil", place2: "brazil" },
{ name: "teste", age: 30, place: "brazil", place2: "brazil" }
];
const columns = [
{
dataField: "name",
text: "Nome",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
},
{
dataField: "age",
text: "Idade",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
},
{
dataField: "place",
text: "Localidade",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
},
{
dataField: "place2",
text: "2",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
}
];
return (
<div className="App">
<Table data={data} columns={columns} />
</div>
);
}
Make following changes to Table.js :
import React from "react";
// import { Container } from "./styles";
const Table = ({ data, columns }) => {
return (
<>
<thead>
<tr>
{columns.map(header => {
return <th>{header.text}</th>;
})}
</tr>
</thead>
<tbody>
{data.map(dado => {
return (
<tr>
{columns.map((column, index) => {
let { Mask } = column;
console.log(column["Mask"]);
Mask = {
...Mask,
props: { children: dado[Object.keys(dado)[index]] }
};
/*------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^
above you can see how we can set the children props of button
*/
return <td>{Mask}</td>;
})}
</tr>
);
})}
</tbody>
</>
);
};
export default Table;
Full working example: Stackblitz
<Mask /> is applicable to function or class components only, so Mask should be either a function or a class. In the example above it is a React element, not a React component.
Please, take a look at this article to understand the difference.
So in order to render it, it is just necessary to use it as a value, like this:
{columns.map((column) => {
var { Mask } = column;
console.log(Mask);
return (
<td>
{Mask}
</td>
);
})}
UPDATE: I didn't fully understand the question at first. So in order to pass data to Mask, you can use an approach from another answer or turn it into a function like this:
const columns = [
{
dataField: "name",
text: "Nome",
Mask: ({ children }) => (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{children}
</button>
)
},
...
];
and render it as you did in your question:
{data.map((dado) => {
return (
<tr>
{columns.map((column) => {
var { Mask, dataField } = column;
console.log(Mask);
return (
<td>
<Mask>
{dado[dataField]}
</Mask>
</td>
);
})}
</tr>
);
})}

Change the displayed data dynamically in react-table v7

Not sure if there is a good way to change the displayed data dynamically. For instance I would like boolean values to be displayed in the table as icons and without slowing down the rendering process too much. I'm using the latest version of react-table v7. I have added code from the Table component Thanks
import React from "react";
import { useTable, useRowSelect, useSortBy, usePagination } from "react-table";
import { CSVLink, CSVDownload } from "react-csv";
import { navigate } from "#reach/router";
function handleColumnsClick(url, id) {
if (url && id) navigate(`${url}${id}`);
}
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
);
}
);
function Table({ columns, data, allowSelection, direct }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
selectedFlatRows,
// pagination
page, // Instead of using 'rows', we'll use page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { selectedRowIds, pageIndex, pageSize },
} = useTable(
{
columns,
data,
initialState: { pageIndex: 0 },
},
useSortBy,
usePagination,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
{allowSelection && (
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
)}
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
{allowSelection && (
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
)}
</div>
),
},
...columns,
]);
}
);
// Render the UI for your table
return (
<>
<table
className="table table-sm table-striped table table-hover"
{...getTableProps()}
>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th
className="checkbox"
{...column.getHeaderProps(column.getSortByToggleProps())}
>
{column.render("Header")}
{/* Add a sort direction indicator */}
<span>
{column.isSorted
? column.isSortedDesc
? " 🔽"
: " 🔼"
: ""}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td
onClick={(e) =>
handleColumnsClick(
direct[1],
cell.row.original[direct[0]]
)
}
{...cell.getCellProps()}
>
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<nav aria-label="Page navigation example">
<ul className="pagination justify-content-center">
<li className="page-item ">
<a
className="page-link"
tabIndex={-1}
aria-disabled="true"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
>
{"<< First Page"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
{"<"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => nextPage()}
disabled={!canNextPage}
>
{" >"}
</a>
</li>
<li className="page-item">
<a
className="page-link"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
>
{"Last Page >>"}
</a>
</li>
</ul>
<ul className="pagination justify-content-right">
<div className="pagination">
<span>
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<select
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</ul>
<p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
</nav>
<pre>
{/* Display selected rows for debugging */}
{/* <code>
{JSON.stringify(
{
selectedRowIds: selectedRowIds,
"selectedFlatRows[].original": selectedFlatRows.map(
(d) => d.original
),
},
null,
2
)}
</code> */}
</pre>
</>
);
}
function setColumns(props) {
const { data } = props;
if (data !== undefined && data.length !== 0) {
const columnsKeys = Object.keys(data[0]);
return columnsKeys.map((column) => {
return {
Header: column,
accessor: column,
};
});
}
}
function SelectTable(props) {
const columns = props.columns ? props.columns : setColumns(props);
const { data, allowSelection, direct } = props;
const addSelection = props.allowSelection ? props.allowSelection : false;
if (columns && data.length)
return (
<>
<Table
columns={columns}
data={data}
allowSelection={addSelection}
direct={direct}
/>
<button className="btn-outline btn-sm">
<CSVLink data={data}>Export to CSV </CSVLink>
</button>
</>
);
else return null;
}
export default SelectTable;

How to change these two functional components(TableData.js and TableSearch.js) to class components?

I need to make the functional components (TableSearch.js and TableData.js ) to class components, first class component should be called TableSearch and second TableData. I just started to study the React and Redux, so it's still hard for me, also the complexity is that in these functions the object is passed by an argument (destructuring).
TableSearch.js:
import React from "react";
export default ({ value, onChange, onSearch }) => {
return (
<div className="tableSearch">
<input type="text" className="searchInput" onChange={onChange} value={value} placeholder="Search by flight"/>
<button className="buttonSearch" onClick={() => onSearch(value)}>Search</button>
</div>
);
};
TableData.js:
import React from "react";
export default ({ data }) => (
<div className="tableContainer">
<table className="table">
<thead>
<tr>
<th>Terminal</th>
<th>Gate</th>
<th>Time</th>
<th>Destination</th>
<th>Airline</th>
<th>Flight</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{data.map(item => {
const dt = new Date(item.actual);
const mins = dt.getMinutes();
return (
<tr key={item.ID}>
<td>{item.term}</td>
<td>{item.gateNo}</td>
<td>{`${dt.getHours()}:${mins < 10 ? '0' : ''}${mins}`}</td>
<td>
{item["airportToID.city_en"]
? item["airportToID.city_en"]
: item["airportFromID.city_en"]}
</td>
<td>{item.airline.en.name}</td>
<td>{item["planeTypeID.code"]}</td>
<td>{item.status}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
How to change these two functional components(TableData.js and TableSearch.js) to class components?
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
I will also write the main component App.js(for more information)
import React from "react";
import { Component } from "react";
import { connect } from "react-redux";
import { fetchData } from "../actions";
import { filter } from "../actions";
import { setSearch } from "../actions";
import TableData from "../components/TableData";
import TableSearch from "../components/TableSearch";
import Header from "../components/Header";
import Footer from "../components/Footer";
import "../components/app.css";
export function searchFilter(search, data) {
return data.filter(n => n["planeTypeID.code"].toLowerCase().includes(search));
}
const days = ["12-11-2019", "13-11-2019", "14-11-2019"];
class Root extends React.Component {
componentDidMount() {
this.props.onFetchData(days[this.props.propsReducer.day]);
}
render() {
const { onFilter, onSetSearch, onFetchData } = this.props;
const { search, shift, data, filteredData } = this.props.propsReducer;
return (
<div>
<div className="content">
<Header/>
<br/>
<div className="searchTitle">SEARCH FLIGHT</div>
<br/>
<TableSearch value={search} onChange={e => onSetSearch(e.target.value)}
onSearch={value => onFilter({ search: value })}/>
<br/>
<br/>
<div className="buttonShift">
{data && Object.keys(data).map(n => (
<button data-shift={n} onClick={e => onFilter({ shift: e.target.dataset.shift })} className={n === shift ? "active" : "noActive"}>
{n}
</button>
))}
</div>
<div className="row">
<span className="title">Yesterday: </span><span className="title">Today: </span><span className="title">Tomorrow: </span>
</div>
<div className="buttonDays">
{days && days.map((day, i) => (
<button key={day} onClick={() => onFetchData(day)} className="buttonDaysOne">
{day}
</button>
))}
</div>
{data && <TableData data={filteredData} />}
</div>
<Footer/>
</div>
);
}
}
const mapStateToProps = state => state;
const matchDispatchToProps = dispatch => ({
onFilter: args => dispatch(filter(args)),
onSetSearch: search => dispatch(setSearch(search)),
onFetchData: day => dispatch(fetchData(day))
});
export const ConnectedRoot = connect(
mapStateToProps,
matchDispatchToProps
)(Root);
I don't know why you want to do it hope you have a good reason
but this how you should do it
TableSearch.jsx
import React from "react";
export default class TableSearch extends React.Component {
render() {
const { value, onChange, onSearch } = this.props;
return (
<div className="tableSearch">
<input type="text" className="searchInput" onChange={onChange} value={value} placeholder="Search by flight" />
<button className="buttonSearch" onClick={() => onSearch(value)}>Search</button>
</div>
);
}
};
TableData.jsx
import React from "react";
export default class TableData extends React.Component {
render() {
const { data } = this.props;
return (
<div className="tableContainer">
<table className="table">
<thead>
<tr>
<th>Terminal</th>
<th>Gate</th>
<th>Time</th>
<th>Destination</th>
<th>Airline</th>
<th>Flight</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{data.map(item => {
const dt = new Date(item.actual);
const mins = dt.getMinutes();
return (
<tr key={item.ID}>
<td>{item.term}</td>
<td>{item.gateNo}</td>
<td>{`${dt.getHours()}:${mins < 10 ? '0' : ''}${mins}`}</td>
<td>
{item["airportToID.city_en"]
? item["airportToID.city_en"]
: item["airportFromID.city_en"]}
</td>
<td>{item.airline.en.name}</td>
<td>{item["planeTypeID.code"]}</td>
<td>{item.status}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}

React JS Sortable Form Fields as Components

I'm trying to develop a fairly simplistic E-Mail template creator with React JS. I'm using the "react-sortable-hoc" library as a means to handle the ordering of elements on the page.
The goal is to allow users to create "Rows", rearrange rows, and within each row, have multiple "Columns" that can contain components like images, textboxes, etc...
But I keep running into the same issue with Sortable libraries. Form fields cannot maintain their own "state" when being dragged up or down. The State of a Component in React JS seems to be lost when it's in a draggable component. I've experienced similar issues with JQuery UI's Sortable but it required an equally ridiculous solution. Is it common to find that form fields are simply super difficult to rearrange?
As a "proof of concept", I am using a complex JSON object that stores all the information in the Letter.js component and passes it down as Props which are then passed down to each component. But as you can tell, this is becoming cumbersome.
Here is an example of my Letter component that handles the JSON object and sorting of Rows:
import React, {Component} from 'react';
import {render} from 'react-dom';
import {
SortableContainer,
SortableElement,
arrayMove
} from 'react-sortable-hoc';
import Row from './Row';
const SortableItem = SortableElement(({row, rowIndex, onChange, addPart}) => {
return (
<li>
<Row
row={row}
rowIndex={rowIndex}
onChange={onChange}
addPart={addPart} />
</li>
)
});
const SortableList = SortableContainer(({rows, onChange, addPart}) => {
return (
<ul id="sortableList">
{rows.map((row, index) => {
return (
<SortableItem
key={`row-${index}`}
index={index}
row={row}
rowIndex={index}
onChange={onChange}
addPart={addPart}
/> )
})}
</ul>
);
});
class Letter extends Component {
constructor(props) {
super(props);
this.state = {
rows: [],
}
this.onSortEnd = this.onSortEnd.bind(this);
this.onChange = this.onChange.bind(this);
this.addRow = this.addRow.bind(this);
this.addPart = this.addPart.bind(this);
}
addPart(event, index, value, rowIndex, columnIndex) {
console.log(value);
var part = {};
if(value === 'Text') {
part = {
type: 'Text',
value: ''
}
} else if(value === 'Image') {
part = {
type: 'Image',
value: ''
}
} else {
part = {
type: 'Empty',
}
}
const { rows } = this.state;
rows[rowIndex][columnIndex] = part;
this.setState({ rows: rows })
}
onChange(text, rowIndex, columnIndex) {
const { rows } = this.state;
const newRows = [...rows];
newRows[rowIndex][columnIndex].value = text;
this.setState({ rows: newRows });
}
addRow(columnCount) {
var rows = this.state.rows.slice();
var row = [];
for(var i = 0; i < columnCount; i++) {
var part = {
type: 'Empty',
}
row.push(part);
}
rows.push(row);
this.setState({ rows: rows })
}
onSortEnd = ({oldIndex, newIndex}) => {
this.setState({
rows: arrayMove(this.state.rows, oldIndex, newIndex),
});
};
render() {
console.log(JSON.stringify(this.state.rows));
const SideBar = (
<div className="sideBar">
<h3>Add a Row</h3>
<button className="uw-button" onClick={() => this.addRow(1)}>1 - Column</button><br/><br/>
<button className="uw-button" onClick={() => this.addRow(2)}>2 - Column</button><br/><br/>
<button className="uw-button" onClick={() => this.addRow(3)}>3 - Column</button>
<hr />
</div>
);
if(this.state.rows.length <= 0) {
return (
<div className="grid">
<p>This E-Mail is currently empty! Add some components to make a template.</p>
{SideBar}
</div>
)
}
return (
<div className="grid">
<SortableList
rows={this.state.rows}
onChange={this.onChange}
addPart={this.addPart}
lockAxis="y"
useDragHandle={true}
onSortStart={this.onSortStart}
onSortMove={this.onSortMove}
onSortEnd={this.onSortEnd}
shouldCancelStart={this.shouldCancelStart} />
{SideBar}
</div>
);
}
}
export default Letter;
And here is an example of Row:
import React, { Component } from 'react';
import { Text, Image } from './components/';
import { SortableHandle } from 'react-sortable-hoc';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
const DragHandle = SortableHandle(() => <span className="dragHandle"></span>);
class Row extends Component {
constructor(props) {
super(props);
}
render() {
if(this.props.row !== undefined && this.props.row.length > 0) {
const row = this.props.row.map((column, columnIndex) => {
if(column.type === 'Empty') {
return (
<MuiThemeProvider key={columnIndex}>
<div className="emptyColumn">
<Card>
<DragHandle />
<CardTitle title="Empty Component"/>
<DropDownMenu value={"Empty"} onChange={(event, index, value) => this.props.addPart(event, index, value, this.props.rowIndex, columnIndex)}>
<MenuItem value={"Empty"} primaryText="Empty" />
<MenuItem value={"Text"} primaryText="Textbox" />
<MenuItem value={"Image"} primaryText="Image" />
</DropDownMenu>
</Card>
</div>
</MuiThemeProvider>
)
} else if(column.type === 'Text') {
return (
<MuiThemeProvider key={columnIndex}>
<div className="textColumn">
<Card>
<DragHandle />
<CardTitle title="Textbox"/>
<DropDownMenu value={"Text"} onChange={(event, index, value) => this.props.addPart(event, index, value, this.props.rowIndex, columnIndex)}>
<MenuItem value={"Empty"} primaryText="Empty" />
<MenuItem value={"Text"} primaryText="Textbox" />
<MenuItem value={"Image"} primaryText="Image" />
</DropDownMenu>
<Text
value={this.props.row[columnIndex].value}
onChange={this.props.onChange}
columnIndex={columnIndex}
rowIndex={this.props.rowIndex} />
</Card>
</div>
</MuiThemeProvider>
)
} else if(column.type === 'Image') {
return (
<MuiThemeProvider key={columnIndex}>
<div className="textColumn">
<Card>
<DragHandle />
<CardTitle title="Image"/>
<DropDownMenu value={"Image"} onChange={(event, index, value) => this.props.addPart(event, index, value, this.props.rowIndex, columnIndex)}>
<MenuItem value={"Empty"} primaryText="Empty" />
<MenuItem value={"Text"} primaryText="Textbox" />
<MenuItem value={"Image"} primaryText="Image" />
</DropDownMenu>
<Image
columnIndex={columnIndex}
rowIndex={this.props.rowIndex} />
</Card>
</div>
</MuiThemeProvider>
)
}
})
return (
<div className="row">
{row}
</div>
)
}
return <p>No components</p>;
}
}
export default Row;
Lastly, this is what Text.js looks like
import React, { Component } from 'react';
import ReactQuill from 'react-quill';
class Text extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ReactQuill value={this.props.value}
onChange={(text) => this.props.onChange(text, this.props.rowIndex, this.props.columnIndex)} />
)
}
}
export default Text;
So, I keep having to pass ridiculous parameters to onChange functions and other functions in order to ensure that the state is maintained while sorting and editing. So, how should I be handling this? I don't want Letter.js (which is basically App.js) to handle all of my data handling. I want each component to handle it's own. I want Text.js to handle the onChange effects of its text. But I just can't see a way around passing everything down as props.

Categories

Resources