Does passing data in props duplicate that data? - javascript

I'm building a dashboard in React, and currently I'm looking at building a small button component that will allow a user to download data from a chart as a CSV file. Something like this:
// Dashboard
render(){
const data=makeLargeDataset(); // make an array with 1m rows
return (
<DownloadCSV data={data} />
);
}
// Download Button
onClick(){
if (this.props.data){
convertAndDownload(this.props.data);
}
}
Does this duplicate the 1M rows when passing to the button's prop? If I had to pass a prop through several layers, will it keep duplicating? Is there a smarter way to pass props -- or work with large variables and arrays, in general -- so it isn't as wasteful or inefficient?

Nope, It does not.
Any props passed from a higher order component to a child component always references the same props. It's called Single Source of Truth. You can alter one data point in the HO Component and React as the name tells reacts to only the change and nothing else.
A simple way for holding such big data sets would be to use a store like Redux to keep the data safe but the smarter way would be to create an API or a micro service based on your needs and call that rather than loading a million rows on the browser.

You are not passing the data copy, you are passing the reference.
You could think about paginating your data in some way in order to handle lower sized arrays.

Related

Can I push objects into Vue component data? Wanting to make a table after js filtering/manipulation

I want to make a table. Actually, I am making a website with many pages with many tables, so I wanted to make a table component. The table data has not yet been put into the table because I need to manipulate the data a lot in js.
When js is done with it, I intended to push every row object into the data property of my Vue Component (to then do a v-for in the html to fill the table).
but I cant find anyone pushing data into vue components. Are the examples right under my nose?
if I don't push into components themselves, that means I need to push into the parent vm? which means a new data property per table instance ..?
I am really struggling putting together the bigger picture connection when it comes to connecting Vue with the outputs from js... Looking for any input
Pushing data into a vue component is not a good practice. You would have a lot easier time if you use Vuex for what you're trying to do. Then you could build a closed-loop data system that updates state data in Vuex with mutations and returns the update to component with a getter. Doing all of this within component is possible if you initialize your data property correctly, though.
To actually answer your question, though, you would do it like this:
data () {
return {
myData: [],
someDataObject: null
}
}
...
methods: {
fillData () {
this.myData.push(this.someDataObject);
}
}
And in template:
...
<div v-for(item in myData, index) :key:item item:item>
<input type:'text' v-model="someDataObject">
<button #click="fillData();"></button>
<p>{{myData[0]}}</p>
</div>
...

How to avoid duplicating logic in react data table

I'm looking for help with structuring an entity data table component in react.
I'm using react-virtualized and am trying to determine how to encapsulate the boilerplate functionality used for server-side sorting/filtering/paging/row deletion/etc.
I started out with a simple, self-contained component which only required an API path, and handled everything by itself with local state.
Needing to be able to update/delete/add/refresh records from outside the table component, I moved the table state/logic into a container component that manages the table, along with some buttons and modal windows.
However, now that all the handlers for sorting/filtering/paging are in this container, it seems they would all have to be duplicated in order to be reused for other tables with slightly different requirements, but the exact same sorting/filtering/paging.
How can I contain the boilerplate table logic within the table component, while also being able to update/delete/add/refresh records from outside the component?
To visualize this, here is a contrived example where all the logic and state is encapsulated in the TableWithFilter component:
<SomeEntityList>
<TableWithFilter apiPath={this.props.apiPath}>
<FilterBar />
<Table />
<TableWithFilter>
</SomeEntityList>
the filter method and state could live in TableWithFilter:
TableWithFilter._handleFilter() {
// get filter, inject filter into api path, get records, update state
}
Now, lets say we need to add a modal so that we can edit the values in a row:
<SomeEntityList>
<EditRowModal />
<TableWithFilter>
<FilterBar />
<Table />
<TableWithFilter>
</SomeEntityList>
The state and filter method then have to be moved up to the SomeEntityList component so that the modal can change the records in the table:
SomeEntityList._handleFilter() {
//get filter, inject filter into api path, get records, update state
}
SomeEntityList._editRow() {
// make update call, update state
}
Assuming I'll have dozens of various SomeEntityListA/SomeEntityListB/SomeEntityListC with various difference (some have edit record modals, some have buttons to add new records, etc) how would I avoid duplicating the filtering logic in dozens of places?
The way I see it you have two options.
If the logic for the reusable filters are functional, you could probably move them out of the components entirely and put them in a utils directory or something.
Use higher-order components.
Edit
Why don't you just pass the filter as a param?
Ok, after some time, I decided to circle back to this to help anyone else out.
I've since moved to incorporation redux in the project (for many reasons).
On this topic specifically, I now have a CreateSearchableTable function that acts a factory for creating namespaced SearchableTable reducers. This allows the reuse of all the fetching/infinite scrolling/paging/filtering/sorting logic. I'm currently working on finding the best way to remove duplicated action creators.

React / Apollo shared query state

I have a very simple app, with a list view and a detail view. The list view is wrapped with a graphql query that maps to a data prop containing an array of items. Selecting one item instantiates a detail view, where the id of the item is used to compose another graphql query for that one item.
const ListView = graphql(ALL_ITEMS, ...)(ListViewComponent);
<ListView data={ [....] }>
<Detail>
<Next /> <Prev /> {/* <-- need info from ListView.data */}
</Detail>
{ this.props.data.map(....) }
</ListView>
The trouble I'm having is adding previous/next navigation to that detail view. The detail component is unaware of what is in that data prop containing all the items in the ListView component. In a typical redux application, I would store the items as global application state, and injecting them into a component would be trivial, but Apollo doesn't seem to work that way. Its state tree is not very consumable (e.g. using connect()).
I see a few possible solutions for this, and none of them seem great:
Wrap the prev/next navigation component with a graphql query that looks for currentOffset + 1 and currentOffset -1. This is definitely the most "apollo" solution, but the problem there is that currentOffset is unknown to that component because it doesn't have the data prop. Further, I would have to ensure that any other variables (e.g. filters, sort) on the list view query got passed through, and those are in the obscure apollo state tree, as well.
Listen to the APOLLO_QUERY_RESULT action in my reducer and maintain a second copy of the query results with all the information my other components need.
Use context to share the data prop to children.
Explicitly pass the data prop to children.
Seems like a common scenario. What's the right approach?
Passing data down as a prop would probably be the easiest solution.
One other option you may want to consider is simply wrapping whatever component you want to have access to the same query data with another graphql HOC. If you utilize the same query you use for ListView's HOC (ALL_ITEMS), you can use the data prop in your child just like you do in ListView. The neat thing about this approach is -- as long as you use the default fetchPolicy -- your child component will not trigger a second query against your server; it will simply utilize what's already in the cache.

Structuring my React/Redux app for efficiency

Im building an audio workstation app that will display a table of tracks containing clips. Right now I have a table reducer which returns a table object. The table object contains track objects and the track objects contain clip objects. I have a TableContainer which subscribes to the table store. My issue is I believe my app will be inefficient because it will re render the page every time a clip is added or manipulated. In reality only the particular track in which the clip resides would need to be re rendered right? How can I structure my app so not every little change re renders the entire app?
In the mapStateToProps of any component, don't select parent objects as a whole to send to the component. If possible select specific properties all the way to the leaf values. If your TableContainer's render() itself doesn't use the tracks array them make sure only the sibling properties that you do use get passed.
So instead of:
function mapStateToProps(state, props) {
return {
table: state.tables[props.tableId];
}
}
Do:
function mapStateToProps(state, props) {
let table = state.tables[props.tableId];
return {
name: table.name,
type: table.type
};
This allows React Redux to be more discerning when it comes to determining whether your component needs to be rerendered. It will see that even though the table had changed due to a clip change, neither the name, nor the type has changed.
However, since your Tablecomponent likely renders the Track components as well, you're likely not going to be able to avoid render calls. If any property anywhere up the tree gets altered, the tracks array also gets altered.
The solution in this case is to have the tracksarray not contain the entire track object but instead only a list of track IDs. You can then store the tracks alongside the tables and a change in one won't affect the other. Note that this only works if you do not go and fetch and pass the track object in the mapStateToProps of the Table component. You should make theTrack component in such a way that it accepts its ID instead of the actual object as a prop. This way the Table component is not dependent on the contents of the tracks at all.
The power of react is to re-render only what needs to be (by using the virtual DOM to make the comparison and the shouldComponentUpdate function).
I wouldn't look too much into it before it becomes a performance problem.
If it does, I would store the tracks in a separate directory and don't pass it to the app (main) component. In your Clip component's mapStateToProps function (if you use react-redux), fetch the track from the state as you get it's name from the props. This way if the track changes a lot (because of async fetching of slices for example), only the component will update.

Ensure data "exists". React, or Endpoint?

I have a moreso best practices question regarding Flux/React for a SPA that gets most of it's data from REST endpoints.
In a React components Render function, when I do something like:
<MyComponent data={this.state.data} />
then in the component, I use {this.props.data}, whether for drawing a graph, text, whatever, it obviously causes problems if data is undefined or occasionally, 0.
Assuming this data lives in the store and is fetched aysnc (and thus also won't exist as soon as the app is loaded), where does it make the most sense to "check" or "ensure" data is defined and is a value that won't break child components? Should I init the data structure in the store will all null/empty values, or make my endpoints return the entire data structure in a similar fashion? Or, does it make sense to do data validation in the actual component and litter it with:
if (!(typeof(this.props.data) === "undefined")) {
Render Component as normal
} else {
Return null or an empty div or whatever
}
I feel like there should be a basic solution to this problem, but I feel like I'm constantly chasing down little things breaking on the off chance undefined/null/0 values "get through", for example drawing a D3 donut chart and every value in the array is 0.
Thanks!

Categories

Resources