Access filtered data in ReactTable - javascript

I am using ReactTable, and have filterable set to true. I need to access the data that gets returned after applying filters, so I can generate CSV's of the filtered down data.
Any ideas on how I might be able to get the filtered down data, as JSON? Been messing around here https://react-table.js.org/#/story/custom-filtering, so far haven't found a way to grab the data that has been filtered down.

I have just found the answer by referencing this article
get refer of table like following:
<ReactTable
ref={(r) => {
this.selectTable = r;
}}
...
/>
And in your function
const currentRecords = this.selectTable.getResolvedState().sortedData;

If you are using React 16.7-alpha+ with hooks you can do something like the following too.
import { useState, useRef } from 'react';
const [pageSize, setPageSize] = useState(20);
let reactTable = useRef(null);
<ReactTable
ref={(r) => (reactTable = r)}
onFilteredChange={() => {
setPageSize(reactTable.getResolvedState().sortedData.length);
}}
/>

React table can take a render prop as a child, and you can pick data off the table state that is passed to that render fn,
<ReactTable {...props}>
{(state, makeTable, instance) => {
// take data from state, resolvedData, or sortedData, if order matters (for export and similar)
// you need to call makeTable to render the table
return (
<>
{makeTable()}
{<p>{`Showing ${state.resolvedData.length} entries`}</p>}
</>
);
}}
</ReactTable>

I'm using React 16.8 (which has full support for hooks, unlike the alpha answer). Not sure if the syntax changed, but this is what worked for me.
Also I'm using version 6.x of react-table. My enclosing component is a functional component.
const reactTable = React.useRef(null)
return <ReactTable ref={reactTable} .... />
I'm also using the checkboxHOC wrapper component like
const CheckboxTable = checkboxHOC(ReactTable)
return <CheckboxTable ref={reactTable} .... />
so to access the data, I have to chain some more calls (getWrappedInstance) like this
reactTable.current.getWrappedInstance().getResolvedState().sortedData
Example from the official documentation: https://github.com/TanStack/table/blob/v6.8.6/docs/src/examples/selecttable/index.js#L104-L111

Related

React lifting states up and down multiple times to filter components

Component diagram:
"Main"
|--"Side"--"CategoryPicker"
|
|--"ItemBoard"
categoryPicker gets the chosen value.
const filterResultHandler = (e) => {
props.onFilterChange(e.target.value);}
...
onChange={filterResultHandler}
And lift up the value to Side.
const [filterState, setFilterState] = useState("all");
const onFilterChangeHandler = () => { props.onPassData(setFilterState);};
...
<CategoryPicker selected={filterState} onFilterChange={onFilterChangeHandler} />
Then I repeat to lift value to the Main.
(Up to this point I have console.log the value and it seemed OK.)
const [recData, setRecData] = useState("all");
const onFilterChangeHandler = (passedData) => {
setRecData(passedData);};
<Side onPassData={onFilterChangeHandler} selected={recData} />
Then pass it down to Itemboard as a prop.
<ItemBoard items={items} recData={recData} />
In ItemBoard I am trying to filter the array then compare to later map it and display filtered components.
const filteredProducts = props.items.filter((product) => {
return (product.cat.toString() === props.recData)})
{filteredProducts.map((product, index) => (
<Item cat={product.cat} />
))}
Warning: Cannot update a component (Side) while rendering a different component (Main). To locate the bad setState() call inside Main
Where am I loosing my logic?
PS.
Trying to focus on understanding how lifting up and passing props works, not looking for better solutions to the problem right now.
you have the bad setState use in this code:
const [recData, setRecData] = useState("all");
const onFilterChangeHandler = (passedData) => {
setRecData(passedData);};
<Side onPassData={onFilterChangeHandler} selected={recData} />
why you are passing this selected={recData} data again to Side component, you are updating the state of Main component from Side component and passing the selected={recData} again, remove this and try again

Conditionally mapping two different arrays into a React component depending on boolean value

I currently have an array called options which is mapped into a React component like so:
{options.map((option) => (
<Component key={option.code}></Component>
))}
However, I am trying to expand my component so that if a boolean value: shouldCount is true, then the optionsWithCount array is passed to the component instead of the options array. The component above has a lot more props etc than shown so I'd like to avoid just repeating the component with all of its props if possible.
Can anyone suggest a possible way of going about this?
Would using a utils function getOptions() work? It would take in the shouldCount flag as input and return the desired array. Example:
{
getOptions(shouldCount).map((option) => (
<Component key={option.code}></Component>
));
}
const getOptions = (shouldCount) => {
return shouldCount ? optionsWithCount : options;
}
You can update the options variable conditionally so you don't have to rewrite the same code again.
Make options a state;
const [options, setOptions] = useState([])
And use useEffect to update it;
useEffect(()=>{
if(shouldCount) setOptions([1,2,3,4,5])
else setOptions([5,6,7,8,9])
}, [shouldCount])
So that way, when shouldCount changed it will trigger useEffect and change optinos values.

Can't update parent state from child using functional components

I am having an issue with my React app. I am trying to set the state of the parent component based on the child component's value. I can see in the dev tools and log window that the child's value is being received by the parent; however, the setState is not working as it should. I have tried creating a separate function just to set the values; hoping for it to act as a middleware but no luck.
I have been through about a couple of StackOverflow threads but not many cater for functional components. I found the following codegrepper snippet for reference but it does not help either.
link: https://www.codegrepper.com/code-examples/javascript/react+function+component+state
Most of the threads deal with how to get the value to the parent component; however, my issue is more "setting the state" specific.
import React, { useEffect, useState } from "react";
import Character from "../component/Character";
import Filter from "../component/Filter";
import Pagination from "../component/Pagination";
import axios from "axios";
import "./Home.css";
const Home = (props) => {
const [API, setAPI] = useState(`https://someapi.com/api/character/?gender=&status=&name=`);
const [characterData, setCharacterData] = useState([]);
const [pagination, setPagination] = useState(0);
const makeNetworkRequest = (data) => {
setAPI(data);
setTimeout(() => {
axios.get(data).then(resp => {
setPagination(resp.data.info)
setCharacterData(resp.data.results)
})
}, 1000)
}
const handleFormCallBack = (childData) => {
setAPI(childData);
makeNetworkRequest(API);
console.log(`Parent handler data ${childData}`)
console.log(`Parent handler API ${API}`)
}
useEffect(() => {
makeNetworkRequest(API)
}, [characterData.length]);
const mappedCharacters = characterData.length > 0 ? characterData.map((character) => <Character key={character.id} id={character.id} alive={character.status} /* status={<DeadOrAlive deadoralive={character.status} /> }*/ gender={character.gender} name={character.name} image={character.image} />) : <h4>Loading...</h4>
return (
<div className="home-container">
<h3>Home</h3>
<Filter parentCallBack={handleFormCallBack} />
<div className="characters-container">
{mappedCharacters}
</div>
{/* <Pagination pages={pagination.pages}/> */}
</div>
)
}
export default Home;
In the code above I am using a callback function on the parent named "handleFormCallBack", mentioned again below to get the information from the child filter component. When I log the value, the following results are being generated.
const handleFormCallBack = (childData) => {
setAPI(childData);
makeNetworkRequest(API);
console.log(`Parent handler data ${childData}`)
// Parent handler data https://someapi.com/api/character/?gender=&status=&name=charactername
console.log(`Parent handler API ${API}`)
// Parent handler API https://someapi.com/api/character/?gender=&status=&name=
}
I am not sure what I am doing wrong but any sort of help would be much appreciated.
Kind Regards
useState works pretty much like setState and it is not synchronous, so when you set the new value using setAPI(childData); react is still changing the state and before it actually does so both of your console.log() statements are being executed.
Solution - after setting the new value you need to track if it has changed, so use a useEffect hook for the endpoint url and then when it changes do what you want.
useEffect(() =< {
// do anything you want to here when the API value changes. you can also add if conditions inside here.
}, [API])
Just to check what I have explained, after calling setAPI(childData); add a setTimeout like
setTimeout(() => {
// you will get new values here. this is just to make my point clear
console.log(Parent handler data ${childData})
console.log(Parent handler API ${API})
}, 5000);

Calling a tabulator table function in React

I am using Tabulator under React, with the react-tabulator module.
I am missing something very basic, likely due to my new knowledge of React. Implementing this module, I know how to connect a tabulator callback to a javascript function. But I don't know how to call a tabulator method. For instance:
const options = {
height: '100%',
ajaxURL: 'http://example.com',
ajaxProgressiveLoad: 'scroll',
ajaxError: (error) => {
console.log('ajaxError ', error);
},
};
...
<ReactTabulator
columns={columns}
layout="fitColumns"
data={[]}
options={options}
/>
Here the ajaxError callback is passed to ReactTabulator, and called when appropriate.
Now, the tabulator module has lots of methods, for instance setData. Outside of React, this would be used as follows:
var table = new Tabulator("#example-table", {
ajaxURL:"http://www.getmydata.com/now", //ajax URL
});
...
table.setData("http://www.getmydata.com/now");
How do I translate this into the React world (in a hook environment), since I don't have direct access to the equivalent of a 'table' object? Do I need to get to my tabulator object by using getElementById or something similar?
I believe that the solution is to use a ref as described here:
https://reactjs.org/docs/refs-and-the-dom.html
, in the section:
Refs and Function Components
By default, you may not use the ref attribute on function components because they don’t have instances:
...
You can, however, use the ref attribute inside a function component as long as you refer to a DOM element or a class component:
Using this approach I am able to access my tabulator setData method.
const tableDisplay = (props) => {
const tableRef = createRef();
// I can now use setData in various effects:
// if ((tableRef.current) && (tableRef.current.table)) {
// tableRef.current.table.setData();
return (
<ReactTabulator
ref={tableRef}
columns={columns}
layout="fitColumns"
data={[]}
options={options}
/>
);
};
This works just fine, I am just not sure this is the "clean" way to go about it?
You need to fetch the api from useEffect and useState its result. Heres an example
import React,{ useState, useEffect }from "react"
import { ReactTabulator } from "react-tabulator"
const Dashboard = props => {
const [data,setData] = useState([])
const getData = async () => {
const result = await fetch("http://www.getmydata.com/now") // this is where you call the api and get its result
setState(result.data) // this is where you set the result to your state
}
useEffect(()=>{
getData()
},[])
const columns=[
{
title:"name",
field:"name",
},
{
title:"Number of Request"
field:"noOfRequest",
}
]
return (
<ReactTabulator
columns={columns}
layout="fitColumns"
data={data} // here is the state of the table
options={options}
/>
)
}
export default Dashboard
This is how you get data from an api in React.

Use dynamically created react components and fill with state values

Below is a proof of concept pen. I'm trying to show a lot of input fields and try to collect their inputs when they change in one big object. As you can see, the input's won't change their value, which is what I expect, since they're created once with the useEffect() and filled that in that instance.
I think that the only way to solve this is to use React.cloneElement when values change and inject the new value into a cloned element. This is why I created 2000 elements in this pen, it would be a major performance hog because every element is rerendered when the state changes. I tried to use React.memo to only make the inputs with the changed value rerender, but I think cloneElement simply rerenders it anyways, which sounds like it should since it's cloned.
How can I achieve a performant update for a single field in this setup?
https://codepen.io/10uur/pen/LYPrZdg
Edit: a working pen with the cloneElement solution that I mentioned before, the noticeable performance problems and that all inputs rerender.
https://codepen.io/10uur/pen/OJLEJqM
Here is one way to achieve the desired behavior :
https://codesandbox.io/s/elastic-glade-73ivx
Some tips :
I would not recommend putting React elements in the state, prefer putting plain data (array, objects, ...) in the state that will be mapped to React elements in the return/render method.
Don't forget to use a key prop when rendering an array of elements
Use React.memo to avoid re-rendering components when the props are the same
Use React.useCallback to memoize callback (this will help when using React.memo on children)
Use the functional form of the state setter to access the old state and update it (this also helps when using React.useCallback and avoid recreating the callback when the state change)
Here is the complete code :
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const INPUTS_COUNT = 2000;
const getInitialState = () => {
const state = [];
for (var i = 0; i < INPUTS_COUNT; i++) {
// Only put plain data in the state
state.push({
value: Math.random(),
id: "valueContainer" + i
});
}
return state;
};
const Root = () => {
const [state, setState] = React.useState([]);
useEffect(() => {
setState(getInitialState());
}, []);
// Use React.useCallback to memoize the onChangeValue callback, notice the empty array as second parameter
const onChangeValue = React.useCallback((id, value) => {
// Use the functional form of the state setter, to update the old state
// if we don't use the functional form, we will be forced to put [state] in the second parameter of React.useCallback
// in that case React.useCallback will not be very useful, because it will recreate the callback whenever the state changes
setState(state => {
return state.map(item => {
if (item.id === id) {
return { ...item, value };
}
return item;
});
});
}, []);
return (
<>
{state.map(({ id, value }) => {
// Use a key for performance boost
return (
<ValueContainer
id={id}
key={id}
onChangeValue={onChangeValue}
value={value}
/>
);
})}
</>
);
};
// Use React.memo to avoid re-rendering the component when the props are the same
const ValueContainer = React.memo(({ id, onChangeValue, value }) => {
const onChange = e => {
onChangeValue(id, e.target.value);
};
return (
<>
<br />
Rerendered: {Math.random()}
<br />
<input type="text" value={value} onChange={onChange} />
<br />
</>
);
});
ReactDOM.render(<Root />, document.getElementById("root"));

Categories

Resources