How to pass props from unrelated component - reactjs

I need to plot a table using data from header component ( 2 drop-downs & one Apply button), while header, table section & footer are unrelated to each other
I have tried to create an array in separate Utils file, which is populated when Apply button is hit, it is passed as
<Table data={utils.sortArray}/>
While data is populating sortArray when Apply button is hit in header component, it is showing length 0 Still
When Apply button is hit, new array data should get passed to table component

If you need the table to update based on input in the header the components aren't really unrelated (they are related through the data they have in common / are sharing).
To communicate between the two you will need a way to pass the data - the logical approach is to have the parent component coordinate between the two as it is contextually aware of both. In this case, you can:
Pass callback a prop to your header component that you call with the required data
Store the data sent in the callback in the parent's state
Pass the state data to your Table.
E.g., in your parent component:
state = {
sortArray: '', // What ever your default value is
}
onSort = (sortArray) => {
this.setState({
sortArray,
});
}
render() {
...
<Header onSort={this.onSort} />
...
<Table sortArray={this.state.sortArray} />
...
}
And then call onSort in your header with the required value as needed.

Related

React table does not refresh when data in a cell changes

I have a React component in which I have a list of items that I store as a dictionary:
const [list, setList] = useState<{ [id: number]: MyProps }>({});
and I render this list as a table in the render method:
{Object.entries(list).map(v =>
<MyTableRow
// #ts-ignore
id={v[0]}
start_date={v[1].start_date}
end_date={v[1].end_date}
dollar_amount={v[1].dollar_amount}
key={v[0]} />
)}
MyTableRow is another React component and basically returns a HTML tr element whose columns are populated based on props passed to it.
So far so good and I have seen that when I add items to the list, the UI gets updated without me having to call setList.
Once I add a new item to the list, I also make some AJAX calls to fetch some additional pieces of information. This is when the problem starts to kick in. When the AJAX call completes, I update the data e.g., as follows:
var e = list[id];
e.dollar_amount = new dollar amount
But the UI does not refresh. I do not want to copy the entire list which could be quite big into a new object and then call setList. The list might have 100 elements in it. Each element corresponds to a row with say 10 columns. I want to be able to update a cell in the table and only that cell should re-render in the UI. Not the whole table. I thought that was the whole value proposition of React.
How can I make it work as described?
PS: I don't want to be making AJAX calls in the MyTableRow child component because the AJAX calls post data into a database and React can call MyTableRow many times for the same data which would result in duplicate records being added to the database.

React: Input is rendering after each character

I'm building a table with React and Material UI 5, but I have a problem with the input when I'm writing. The input loses the focus in each character digited.
How can I handle with component re-rendering?
The complete code: https://codesandbox.io/s/mytable-rn98b
You are defining new component types in the middle of rendering MyTable. Every time MyTable renders, you define a new TableToolbar, new HeaderList, new RowList, new CellList, new CellEdit. These functions may have the same text as the previous ones, but they do not pass a reference equality check (===) with their previous incarnations, so they are different types of components to react. Since they are different types, react must unmount the old ones and mount the new ones, which wipes out their state and focus.
If you want these to be their own components, they need to be defined just once, outside of the main component. This will require a bit of rewriting, since you can no longer rely on closure variables to access MyTable's data. Instead, pass the needed data as props.
For example, CellList should change from this:
const CellList = ({ row }) =>
headers.map((header) => (
<TableCell key={header.prop}>{row[header.prop]}</TableCell>
));
To this (and make sure to move it outside of MyTable):
const CellList = ({ row , headers }) => {
return (
<>
{headers.map((header) => (
<TableCell key={header.prop}>{row[header.prop]}</TableCell>
))}
<>
)
}

How to model 1'000'000'000 cells in React?

So I want to display a large table, say 1 billion cells.
The component receives remote updates, each addressing one cell.
Each cell is represented via a div
Here's a naive implementation, which won't be practical, because on each update a large array is created and all cells are updated.
const Table = props => {
const [cells, setCells] = useState(new Array[1_000_000_000])
// ... 'useEffect()' receives remote data and 'setCells()' it.
return cells.map(cell => <div id={cell.id}>{cell.text}</div>)
}
How to implement this efficiently so that performance is top notch?
Ideally, I would like that on each update only 1 array element is updated and only 1 table cell in DOM is updated 🤷‍♂️ Ideally the solution should be O(1) and work great for tables of 10 to 1'000'000'000 cells. Is it possible in React?
By 1'000'000'000 cells I mean a large number of cells that browser can display at once if cells are created and updated with Vanilla JavaScript.
I'm looking not for a library but for a pattern. I want to find out what is the general approach in React for such cases. It is obvious how to do this in Vanilla JavaScript, you just create divs and then access the required div and update its innerText 🤷‍♂️. But how this case can be modeled in React?
For very long lists, "virtualized list" is the typical approach so that only some of the cells are actually mounted and the rest are temporarily rendered with placeholders. A couple libraries that implement this are:
https://react-window.now.sh/
https://bvaughn.github.io/react-virtualized
If you are looking for strictly O(1) then typical React patterns may not suitable since React cannot partially render a component. In other words, the render method of your Table component will need to iterate over the list in O(n). Virtualization of the list could reduce from O(size of list) to O(size of window).
You may be able to workaround this by maintaining a list of React refs of each mounted cell, then calling an update method on the single cell by index to trigger a re-render of a single cell, but this is likely not recommended usage of React.
Another option could be the Vanilla JS approach inside of a stateful React component. You'll want to implement the relevant lifecycle methods for mounting and un-mounting, but the update would be handled in Vanilla JS.
class Table {
constructor(props) {
super(props);
this.container = React.createRef();
}
componentDidMount() {
// Append 1_000_000_000 cells into this.container.current
// Later, modify this.container.current.item(index) text
}
componentWillUnmount() {
// Perform any cleanup
}
render() {
return (
<div ref={this.container} />
)
}
}

React: Passing properties into customComponent Griddle

I got stuck on a problem where I want to give some properties to a customCompontent.
I have a react component scoutingList that prints a list of players who are scouted at the moment. It's also possible to remove a player from this list by clicking on a button.
I also have a griddle component where the table is filled with data from a api call, except for the scout column for this column I made a customComponent.
return (
<Griddle
columns={["scout", "name", "club", "position", "points", "rpoints", "avgpoints", "value"]}
results={this.state.data}
/>
)
This customComponent should render a button with the text "Scout" or "Scouted".
From my parent class I pass a state this.state.scoutedPlayers what I want to pass to my customComponent. I hava found that you can pass extra data through customComponentMetaData, but it doesn't accepts props.
Anyone knows how to pass a property through a customComponent directly ?

Put logic within a stateless child component or send prop values back to stateful parent and perform logic there?

const TrackerRow = (props) => {
const renderCampaignItems = props.group.map(
campaign => <CampaignRow key={campaign.tracker + '|' + campaign.token} name={campaign.tracker} token={campaign.token} />)
return (
<tr>
<td>{renderCampaignItems}</td>
<td><a onClick={props.onDeleteTracker}>Delete</a></td>
</tr>
)
}
I have a series of nested children in an app. The highest level component manages all the state everything else is stateless. Now in the component I've provided code for above I want to carry out a database operation with values that are currently in props.group and then remove the row from a list. Currently the onDeleteTracker is defined at the top level component and passed down the chain to the above component. I'm now considering how do I get the values of this component back to the top level component to tell it which items to delete. Should I just be performing the delete functionality within the TrackerRow component instead of passing the prop values back to the toplevel component as function parameters. Is there any reasoning which method to use here? I'm unclear of the pros and cons of either method (or alternatives.)
Definitely pass the instruction to 'delete item x' up to the component that manages state (or straight through to a store if you had one).
Actually I don't think there's even a choice. Remember that you can't mutate props, and that the data source is represented as state in the parent component.
But are you removing a 'group'? Or are you removing a 'campaign'? From your code, the property group appears to just be an array of campaigns (for what it's worth, I'd suggest always using the plural of the item for arrays, e.g. renaming 'group' to 'campaigns').
So I'll guess that you're actually wanting to remove a campaign, in which case you would call onDeleteTracker from inside the <CampaignRow/> component (when it's clicked or whatever). Which would look something like onClick={() => props.onDeleteTracker(props.id)}.

Resources