How to remove commas from strings in an array? - javascript

I need to remove commas which are in an array of odata objects. Each item has a description which sometimes contains commas. I need to remove those commas.
Here is what I've attempted:
Parent Component:
<CSVComponent capxs={allCaps} />
Child Component:
import { CSVLink } from "react-csv";
export interface ICSVComponentProps {
capxs: IListItem[];
}
export const CSVComponent: React.FunctionComponent<ICSVComponentProps> = (props: ICSVComponentProps) => {
const [results, setResults] = React.useState([]);
const headers = [
{ label: "Id", key: "id" },
{ label: "Title", key: "title" },
{ label: "Description", key: "description" },
];
const getCaps = (c: IListItem[]) => {
const results = [];
for (let i = 0; i <= c.length - 1; i++) {
results[i] = {
id: props.capxs[i].Id,
title: props.capxs[i].Title,
description: props.capxs[i].Description.replace(",",""),
};
}
setResults(results);
};
return (
<DefaultButton className={styles.csvButton}
onClick={() => getCaps(props.capxs)}
>
<CSVLink data={results} headers={headers}>
Create CSV
</CSVLink>
</DefaultButton>
);
Update:
I've updated the code sample above with the Parent component props pass down and more of the child component.
Here is an image showing the array that is created and stored in state. This state is then used by react-csv:
The description value in each item in the array has the comma removed, but react-csv seems to ignore this and it detects the commas, therefore creating incorrect columns in the produced csv file.

If I understand correctly what you need
const memoizedCaps = useMemo<IListItem[]>(() => {
return caps.map(el => ({ ...el, description: el.description.replace(',', '') }));
}, [caps]);
const getCaps = () => {
setResults(memoizedCaps);
};

This issue has been resolved by using:
const getCaps = (caps: IListItem[]) => {
const results = [];
for (let i = 0; i <= caps.length - 1; i++) {
results[i] = {
description: props.capxs[i].Description.replace(/,/g, '""'),
};
}
setResults(results);
};
Not ideal but it's a limitation of react-csv.
Ref: https://github.com/react-csv/react-csv/issues/176
Thanks for all your help.

Related

Ag-grid editable grid adding new row dynamically

I have an editable AgGrid in my functional component as below.
On the last column, I have buttons to Add/Remove rows.
Now I want the Add row to be displayed only for the last row. I am using cellRenderer for the last column.
With the below change, I get the Add button for the last row (i.e. on 2nd row in my case) on initial render. But if I click on Add for this 2nd row, while I get the Add button for the new 3rd row, but it does not get removed for the 2nd row. not sure if I am implementing this in the wrong way.
const MyCmp = (props) => {
const getData = () => {
return [{
id: 0,
firstName: 'F1',
lastName: 'L1'
}, {
id: 1,
firstName: 'F2',
lastName: 'L2',
}];
}
const [myCols, setMyCols] = useState(null);
const [gridData, setGridData] = useState(getData());
const [gridApi, setGridApi] = useState('');
let cellRules = {
'rag-red': params => {
if (params.data.lastName === 'INVALID_VAL') {
return true;
}
}
};
const handleGridReady = (params) => {
setGridApi(params.api);
setMyCols([{
headerName: 'F Name',
field: 'firstName',
editable: true
}, {
headerName: 'L Name',
field: 'lastName',
cellClassRules: cellRules,
editable: true
}, {
headerName: '',
field: 'buttonCol',
cellRenderer: 'customColRenderer',
cellRendererParams: {
addItems: addItems
}
}]
);
};
const createNewRowData = () => {
const newData = {
id: newCount,
firstName: '',
lastName: ''
};
newCount++;
return newData;
}
let newCount = getData().length;
const addItems = (addIndex, props) => {
const newItems = [createNewRowData()];
const res = props.api.applyTransaction({
add: newItems,
addIndex: addIndex,
});
setGridData(...gridData, res.add[0].data); // IS THIS CORRECT ?
if (props.api.getDisplayedRowCount() > props.api.paginationGetPageSize()) {
props.api.paginationGoToPage(parseInt((props.api.getDisplayedRowCount() / props.api.paginationGetPageSize())) + 1);
}
}
const onCellClicked = (e) => {
}
const frameworkComponents = () => {
return {customColRenderer: customColRenderer}
}
return (
<>
<MyAgGrid
id="myGrid"
columnDefs={myCols}
rowData={gridData}
frameworkComponents={{customColRenderer: customColRenderer}}
{...props}
/>
</>
)
}
My customColRenderer is as below;
export default (props) => {
let isLastRow = (props.rowIndex === (props.api.getDisplayedRowCount() -1)) ? true: false;
const addItems = (addIndex) => {
props.addItems(addIndex, props);
}
return (
<span>
{isLastRow ? <button onClick={() => addItems()}>Add</button> : null}
<span><button onClick={() => props.api.applyTransaction({remove: props.api.getSelectedRows()})}>Remove</button>
</span>
);
};
Within the AgGrid React internals a transaction is generated automatically when your rowData is updated, as such you can choose to apply the transaction either through the api, or by updating your state - you shouldn't need to do both (as you're currently doing). Generally with React I'd suggest updating the state to keep your state true to the data displayed in the grid, however that can depend on use case.
As for the further issue of your cell not updating - that'll be due to the fact AgGrid has detected no change in the 'value' of the cell, as it attempts to reduce the amount of unnecessary cell rendering done.
You could attempt to call:
api.refreshCells({ force: true });
After your api call applying the transaction (I'm not sure where this would need to happen using the setState approach).

Need to fix a issue that comming in antd tree component

I am using an antd tree component my issue is if you search something in the search bar then you will get search results in that result if you check and up check any field what happens all previous data get unchecked whatever data is present into the search bar result that only data remain selected if it already select or you just select what my task is I don't want to get unchecked all previously selected checked that only field update that we change right now I don't have any idea how can I fix this if anybody knows anyway, also I added a complete code SandBox link below.
This is my search bar filter code
const hasSearchTerm = (n, searchTerm) =>
n.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1;
const filterData = (arr, searchTerm) =>
arr?.filter(
(n) =>
hasSearchTerm(n.title, searchTerm) ||
filterData(n.children, searchTerm)?.length > 0
);
function filteredTreeData(data, searchString, checkedKeys, setExpandedTree) {
let keysToExpand = [];
const filteredData = searchString
? filterData(data, searchString).map((n) => {
keysToExpand.push(n.key);
return {
...n,
children: filterData(n.children, searchString, checkedKeys)
};
})
: data;
setExpandedTree([...keysToExpand]);
return filteredData;
}
This issue happens when the check or unchecks field after searching in the search bar in this part of the code
const onCheck = (checkedKeysValue) => {
console.log("onCheck", checkedKeysValue);
setCheckedKeys(checkedKeysValue);
};
const Demo = () => {
const [expandedKeys, setExpandedKeys] = useState([]);
const [checkedKeys, setCheckedKeys] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [searchValue, setSearchValue] = useState("");
const [tree, setTree] = useState(treeData);
const onExpand = (expandedKeysValue) => {
console.log("onExpand", expandedKeysValue); // if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck = (checkedKeysValue) => {
console.log("onCheck", checkedKeysValue);
setCheckedKeys(checkedKeysValue);
};
const onSelect = (selectedKeysValue, info) => {
console.log("onSelect", info);
setSelectedKeys(selectedKeysValue);
};
React.useEffect(() => {
const checked = [];
treeData.forEach((data) => {
data.children.forEach((item) => {
if (item.checked) {
checked.push(item.key);
}
});
});
setCheckedKeys(checked);
}, []);
React.useEffect(() => {
if (searchValue) {
const filteredData = filteredTreeData(
treeData,
searchValue,
checkedKeys,
setExpandedKeys
);
setTree([...filteredData]);
} else {
setTree(treeData);
// setExpandedKeys([]);
}
}, [searchValue, checkedKeys]);
return (
<div>
<Search
style={{ marginBottom: 8 }}
placeholder="Search"
onChange={(e) => {
setSearchValue(e.target.value);
}}
/>
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={tree}
/>
</div>
);
};
CodeSandBox Link
Edited Response to fix uncheck issue
If I understood the question correctly, Is the issue with the fact that previous checked items are getting cleared on search and selection of new one?
I think the solution would be to use 2 different separate trees one for filtered and the other for normal.
Did some code changes on top of the sandbox code shared.
I have added a new tree when searchedValue is present.
On checking/unchecking the new filtered tree, the actual entire tree's checked values get updated
When the searched value is empty it would show the actual entire tree
Created a filteredKeys list to solve uncheck issue. Now I am able to select and unselect.
If you play around and refactor a bit, you should be able to achieve what you want.
Adding the same code below.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Tree, Input } from "antd";
const { Search } = Input;
const treeData = [
{
title: "AP Watchlists",
key: "AP Watchlists",
children: [
{ title: "Colo Open Data", key: "Colo Open Data", checked: true },
{
title: "Department and trade",
key: "Department and trade",
checked: true
},
{
title: "North List",
key: "North List",
checked: true
},
{ title: "People's Daily", key: "People's Daily", checked: true }
]
},
{
title: "Af Watchlists",
key: "Af Watchlists",
children: [
{
title: "Service Wanted List",
key: "Service Wanted List",
checked: true
}
]
},
{
title: "EM Watchlists",
key: "EM Watchlists",
children: [
{
title: "National Financing",
key: "National Financing",
checked: true
},
{
title: "Arabia List",
key: "Arabia List",
checked: true
}
]
},
{
title: "Assets List",
key: "Assets List",
children: [
{
title: "National List",
key: "National List",
checked: true
}
]
},
{
title: "New Watchlists",
key: "New Watchlists",
children: [
{ title: "FATR", key: "FATR", checked: true },
{ title: "Internal", key: "Internal", checked: true },
{
title: "OC List (Covers 73 Lists)",
key: "OC List (Covers 73 Lists)",
checked: true
},
{ title: "UN", key: "UN", checked: true },
{
title: "Security List (Covers 18 Lists)",
key: "Security List (Covers 18 Lists)",
checked: true
}
]
}
];
const Demo = () => {
const hasSearchTerm = (n, searchTerm) =>
n.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1;
const filterData = (arr, searchTerm, keys) => {
const result = arr?.filter(
(n) =>
hasSearchTerm(n.title, searchTerm) ||
filterData(n.children, searchTerm, keys)?.length > 0
);
result &&
result.forEach((node) => {
if (keys.indexOf(node?.key) === -1) keys.push(node?.key);
});
return result;
};
function filteredTreeData(data, searchString, checkedKeys, setExpandedTree) {
let keysToExpand = [];
const keys = [];
const filteredData = searchString
? filterData(data, searchString, keys).map((n) => {
keysToExpand.push(n.key);
return {
...n,
children: filterData(n.children, searchString, keys)
};
})
: data;
setExpandedTree([...keysToExpand]);
setFilteredKeys(keys);
return filteredData;
}
const [expandedKeys, setExpandedKeys] = useState([]);
const [checkedKeys, setCheckedKeys] = useState([]);
const [filteredCheckedKeys, setFilteredCheckedKeys] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [searchValue, setSearchValue] = useState("");
const [tree, setTree] = useState(treeData);
const [filteredTree, setFilteredTree] = useState([]);
const [filteredKeys, setFilteredKeys] = useState([]);
const onExpand = (expandedKeysValue) => {
// console.log("onExpand", expandedKeysValue); // if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck = (checkedKeysValue) => {
// console.log("onCheck", checkedKeysValue);
// console.log("checkedKeys", checkedKeys);
setCheckedKeys(checkedKeysValue);
};
const onFilteredTreeCheck = (checkedKeysValue) => {
// console.log("onFilterCheck", checkedKeysValue);
// console.log("filteredcheckedKeys", filteredCheckedKeys);
setFilteredCheckedKeys(checkedKeysValue);
const baseTreeKeys = [...checkedKeys].filter(
(node) => filteredKeys.indexOf(node) === -1
);
console.log("baseTreeKeys", baseTreeKeys);
console.log("checkedKeysValue", checkedKeysValue);
setCheckedKeys([...checkedKeysValue, ...baseTreeKeys]);
};
const onSelect = (selectedKeysValue, info) => {
console.log("onSelect", info);
setSelectedKeys(selectedKeysValue);
};
// React.useEffect(() => {
// const checked = [];
// treeData.forEach((data) => {
// data.children.forEach((item) => {
// if (item.checked) {
// checked.push(item.key);
// }
// });
// });
// setCheckedKeys(checked);
// }, []);
React.useEffect(() => {
setFilteredKeys([]);
if (searchValue) {
const filteredData = filteredTreeData(
treeData,
searchValue,
checkedKeys,
setExpandedKeys
);
setFilteredTree([...filteredData]);
} else {
setTree(treeData);
// setExpandedKeys([]);
}
}, [searchValue, checkedKeys]);
console.log("filteredCHeckedValues", filteredCheckedKeys);
console.log("existing checked values", checkedKeys);
return (
<div>
<Search
style={{ marginBottom: 8 }}
placeholder="Search"
onChange={(e) => {
setSearchValue(e.target.value);
}}
/>
{searchValue ? (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onFilteredTreeCheck}
checkedKeys={filteredCheckedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={filteredTree}
/>
) : (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={tree}
/>
)}
</div>
);
};
ReactDOM.render(<Demo />, document.getElementById("container"));
Let me know if this solves your issue. I am able to select multiple values in subsequent searches without loosing the checked ones.
As mentioned in the API document, filterTreeNode will keep keys from the tree node, and will not hide it.
filterTreeNode
Defines a function to filter treeNodes. When the function returns true, the corresponding treeNode will be checked
If you want to hide tree node, you will have to manually filter it first before before passing it to Tree in loop function, something like:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Tree, Input } from "antd";
const gData = [
{
key: "1",
title: "title 1"
},
{
key: "2",
title: "title 2"
},
{
key: "3",
title: "title 3",
children: [
{
key: "4",
title: "title 4"
},
{
key: "5",
title: "title 5",
children: [
{
key: "6",
title: "title 6"
},
{
key: "7",
title: "title 7"
}
]
}
]
}
];
const { Search } = Input;
const dataList = [];
const generateList = (data) => {
for (let i = 0; i < data.length; i++) {
const node = data[i];
const { key } = node;
dataList.push({ key, title: key });
if (node.children) {
generateList(node.children);
}
}
};
generateList(gData);
const getParentKey = (key, tree) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
const SearchTree = () => {
const [expandedKeys, setExpandedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [searchValue, setSearchValue] = useState("");
const [treeData, setTreeData] = useState(gData);
const onExpand = (expandedKeys) => {
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
const onChange = (e) => {
const value = e.target.value?.toLowerCase();
const expandedKeys = dataList
.map((item) => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, gData);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
if (value) {
const hasSearchTerm = (n) => n.toLowerCase().indexOf(value) !== -1;
const filterData = (arr) =>
arr?.filter(
(n) => hasSearchTerm(n.title) || filterData(n.children)?.length > 0
);
const filteredData = filterData(gData).map((n) => {
return {
...n,
children: filterData(n.children)
};
});
setTreeData(filteredData);
setExpandedKeys(expandedKeys);
setSearchValue(value);
setAutoExpandParent(true);
} else {
setTreeData(gData);
setExpandedKeys([]);
setSearchValue("");
setAutoExpandParent(false);
}
};
const filterTreeNode = (node) => {
const title = node.title.props.children[2];
const result = title.indexOf(searchValue) !== -1 ? true : false;
console.log(searchValue);
console.log(result);
return result;
};
const loop = (data) =>
data.map((item) => {
const index = item.title.indexOf(searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(index + searchValue.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span className="site-tree-search-value">{searchValue}</span>
{afterStr}
</span>
) : (
<span>{item.title}</span>
);
if (item.children) {
return { title, key: item.key, children: loop(item.children) };
}
return {
title,
key: item.key
};
});
return (
<div>
<Search
style={{ marginBottom: 8 }}
placeholder="Search"
onChange={onChange}
/>
<Tree
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
treeData={loop(treeData)}
filterTreeNode={filterTreeNode}
/>
</div>
);
};
ReactDOM.render(<SearchTree />, document.getElementById("container"));

React - Construct a numeric array for a component to a dynamic number

I need to provide a sub component with an array of numbers from state - the array is defined by a provided total number (this.props.totalMarkerNumbers) prop (in string format) - i'm going astray with the syntax construct and wonder if anyone can please point me in the correct direction please?
Heres my code to create the array:
const ArrayCont = () => {
let i = 0;
let totalM = parseInt(this.props.totalMarkerNumbers, 10);
for (i = 0; i < totalM.length; i++) {
let no="''" + i +"''";
return(
{
label:no,
value:no
}
)
}
}
const Markers = () => {
let arrayCont =
return (
[{this.ArrayCont}]
)
}
This would be stored in state as:
class A_Log extends React.Component {
constructor(props) {
super(props);
this.state = {
markerArray:Markers,
};
}
basically if this.props.totalMarkerNumbersprovided '4' i'd want the array to be:
this.state.markerArray = [
{
label: '1',
value: '1',
},
{
label: '2',
value: '2',
},
{
label: '3',
value: '3',
},
{
label: '4',
value: '4',
},
];
theres probably a lot easier way to achieve this - any advice very welcome! Cheers.
I see 2 main issue in your code:
The first is this line:
let no="''" + i +"''";
I suppose you're trying to convert your int into a string, but it's not the correct way, you should instead use the toString function
The second is the return of your ArrayCont function.
With the provided code, you'll return an object containing one label and one value.
If you want to return an array, you have to create one and fill it each time you go through your loop
Here is a basic example:
function ArrayCont() {
let arraySize = parseInt(this.props.totalMarkerNumbers, 10);
const result = [];
for (var i = 1; i <= arraySize; i++) {
result.push({
label: i.toString(),
value: i.toString(),
});
}
return result;
}
Are you open to using a functional react component? Something like this does the trick:
import React, { useState } from 'react';
export default function App() {
const [ inputVal, setInputVal ] = useState('0');
const [ myArray, setMyArray ] = useState([]);
const handleChange = (e) => {
let val = e.currentTarget.value // get input value
setInputVal(val); // set it to state
let newArray = []; // build a new array
for (let i = 0; i < val; i++) { // for each between 0-val...
newArray.push({ // push obj to array
label: i + 1,
value: i + 1,
})
};
setMyArray(newArray); // set the array to state
}
return (
<div className="App">
<input
value={inputVal}
onChange={handleChange}
/>
<p>
{JSON.stringify(myArray)}
</p>
</div>
);
}
I reread your question and see if that you are interested in knowing how to pass props. So I've reworked my above example to show how you might ingest a string from an input, and use that to build (and show) your array.
import React, { useEffect, useState, useCallback } from "react";
/**
* EgChildComponent -- Function
* An example of how to pass state to a child component
* #param {number} inputVal The value of the input.
* #return {JSX.Element} A list of styled paragraph with your content
*/
const EgChildComponent = ({ inputVal }) => {
const [myArray, setMyArray] = useState([]);
const updateArray = useCallback((val) => {
let newArray = []; // build a new array
for (let i = 0; i < val; i++) {
// for each between 0-val...
newArray.push({
// push obj to array
label: i + 1,
value: i + 1
});
}
setMyArray(newArray); // set the array to state
}, [setMyArray]);
useEffect(() => {
if (myArray.length.toString() !== inputVal) {
updateArray(inputVal)
}
}, [inputVal, myArray.length, updateArray]);
const aChild = (childItem) => {
return (
<p key={`child-${JSON.stringify(childItem)}`}>
<b>Label: {childItem.label}</b>, value: {childItem.value}
</p>
);
};
return <>{myArray.map((item) => aChild(item))}</>;
};
/**
* Parent component where the value is set, and passed
* to the child via props, which then dynamically updates
* the array as desired.
*/
export default function App() {
const [inputVal, setInputVal] = useState("");
const handleChange = (e) => {
let val = e.currentTarget.value; // get input value
setInputVal(val); // set it to state
};
return (
<div className="App">
<input value={inputVal} onChange={handleChange} />
{inputVal !== '' && (
<EgChildComponent inputVal={inputVal} />
)}
</div>
);
}
Working CodeSandbox: https://codesandbox.io/s/stack-66696633-updatedarray-vm3rm
Not sure if I understand you right, but I guess this?
const getArr = (x) => Array.from(Array(+x),(_,i)=>({label:String(i+1),value:String(i+1)}))
console.log('Total markers 3:');
console.log(getArr('3'));
console.log('Total markers 5:');
console.log(getArr('5'));
console.log('Total markers 10:');
console.log(getArr('10'));

How to use filter function in multiple states at one time in react native

I want to filter the data from my multiple states at one time. But I am getting the data of only second state.
I have two states and both states are getting seprate data from seprate apis. Now I want to filter the data from it. thank youI don't know what i m doing wrong so pls help me and look at my code.
searchFeatured = value => {
const filterFeatured = (
this.state.latestuploads || this.state.featuredspeakers
).filter(item => {
let featureLowercase = (item.name + " " + item.title).toLowerCase();
let searchTermLowercase = value.toLowerCase();
return featureLowercase.indexOf(searchTermLowercase) > -1;
});
this.setState({
featuredspeakers: filterFeatured,
latestuploads: filterFeatured
});
};
class SearchPage extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
featuredspeakers: [],
latestuploads: [],
};
}
componentDidMount() {
axios
.all([
axios.get(
'https://staging.islamicmedia.com.au/wp-json/islamic-media/v1/featured/speakers',
),
axios.get(
'https://staging.islamicmedia.com.au/wp-json/islamic-media/v1/featured/latest-uploads',
),
])
.then(responseArr => {
//this will be executed only when all requests are complete
this.setState({
featuredspeakers: responseArr[0].data,
latestuploads: responseArr[1].data,
loading: !this.state.loading,
});
});
}
Using the || (OR) statement will take the first value if not null/false or the second. What you should do is combine the arrays
You should try something like
[...this.state.latestuploads, ... this.state.featuredspeakers].filter(item=>{});
Ahmed, I couldn't get your code to work at all - searchFeatured is not called anywhere. But I have some thoughts, which I hope will help.
I see that you're setting featuredspeakers and latestuploads in componentDidMount. Those are large arrays with lots of data.
But then, in searchFeatured, you are completely overwriting the data that you downloaded and replacing it with search/filter results. Do you really intend to do that?
Also, as other people mentioned, your use of the || operator is just returning the first array, this.state.latestuploads, so only that array is filtered.
One suggestion that might help is to set up a very simple demo class which only does the filtering that you want. Don't use axios at all. Instead, set up the initial state with some mocked data - an array of just a few elements. Use that to fix the filter and search functionality the way that you want. Here's some demo code:
import React from 'react';
import { Button, View, Text } from 'react-native';
class App extends React.Component {
constructor(props) {
super(props);
this.searchFeatured = this.searchFeatured.bind(this);
this.customSearch = this.customSearch.bind(this);
this.state = {
loading: false,
featuredspeakers: [],
latestuploads: [],
};
}
searchFeatured = value => {
// overwrite featuredspeakers and latestuploads! Downloaded data is lost
this.setState({
featuredspeakers: this.customSearch(this.state.featuredspeakers, value),
latestuploads: this.customSearch(this.state.latestuploads, value),
});
};
customSearch = (items, value) => {
let searchTermLowercase = value.toLowerCase();
let result = items.filter(item => {
let featureLowercase = (item.name + " " + item.title).toLowerCase();
return featureLowercase.indexOf(searchTermLowercase) > -1;
});
return result;
}
handlePress(obj) {
let name = obj.name;
this.searchFeatured(name);
}
handleReset() {
this.setState({
featuredspeakers: [{ name: 'Buffy', title: 'Slayer' }, { name: 'Spike', title: 'Vampire' }, { name: 'Angel', title: 'Vampire' }],
latestuploads: [{ name: 'Sarah Michelle Gellar', 'title': 'Actress' }, { name: 'David Boreanaz', title: 'Actor' }],
loading: !this.state.loading,
});
}
componentDidMount() {
this.handleReset();
}
getList(arr) {
let output = [];
if (arr) {
arr.forEach((el, i) => {
output.push(<Text>{el.name}</Text>);
});
}
return output;
}
render() {
let slayerList = this.getList(this.state.featuredspeakers);
let actorList = this.getList(this.state.latestuploads);
return (
<View>
<Button title="Search results for Slayer"
onPress={this.handlePress.bind(this, {name: 'Slayer'})}></Button>
<Button title="Search results for Actor"
onPress={this.handlePress.bind(this, {name: 'Actor'})}></Button>
<Button title="Reset"
onPress={this.handleReset.bind(this)}></Button>
<Text>Found Slayers?</Text>
{slayerList}
<Text>Found Actors?</Text>
{actorList}
</View>
);
}
};
export default App;
You should apply your filter on the lists separately then. Sample code below =>
const searchFeatured = value => {
this.setState({
featuredspeakers: customSearch(this.state.featuredspeakers, value),
latestuploads: customSearch(this.state.latestuploads, value)
});
};
const customSearch = (items, value) => {
return items.filter(item => {
let featureLowercase = (item.name + " " + item.title).toLowerCase();
let searchTermLowercase = value.toLowerCase();
return featureLowercase.indexOf(searchTermLowercase) > -1;
});
}

Export to CSV button in react table

Looking for a way to add an "Export to CSV" button to a react-table which is an npmjs package (https://www.npmjs.com/package/react-table).
I need to add a custom button for exporting the table data to an excel sheet in the csv or xls format?
Take a look at this npm library - https://www.npmjs.com/package/react-csv
For example -
import {CSVLink, CSVDownload} from 'react-csv';
const csvData =[
['firstname', 'lastname', 'email'] ,
['John', 'Doe' , 'john.doe#xyz.com'] ,
['Jane', 'Doe' , 'jane.doe#xyz.com']
];
<CSVLink data={csvData} >Download me</CSVLink>
// or
<CSVDownload data={csvData} target="_blank" />
Here is what the integration will look like:
import React from "react";
import "react-dropdown/style.css";
import "react-table/react-table.css";
import ReactTable from "react-table";
import { CSVLink } from "react-csv";
const columns = [
{
Header: "name",
accessor: "name", // String-based value accessors!
},
{
Header: "age",
accessor: "age",
},
];
class AllPostPage extends React.Component {
constructor(props) {
super(props);
this.download = this.download.bind(this);
this.state = {
tableproperties: {
allData: [
{ name: "ramesh", age: "12" },
{ name: "bill", age: "13" },
{ name: "arun", age: "9" },
{ name: "kathy", age: "21" },
],
},
dataToDownload: [],
};
}
download(event) {
const currentRecords = this.reactTable.getResolvedState().sortedData;
var data_to_download = [];
for (var index = 0; index < currentRecords.length; index++) {
let record_to_download = {};
for (var colIndex = 0; colIndex < columns.length; colIndex++) {
record_to_download[columns[colIndex].Header] =
currentRecords[index][columns[colIndex].accessor];
}
data_to_download.push(record_to_download);
}
this.setState({ dataToDownload: data_to_download }, () => {
// click the CSVLink component to trigger the CSV download
this.csvLink.link.click();
});
}
render() {
return (
<div>
<div>
<button onClick={this.download}>Download</button>
</div>
<div>
<CSVLink
data={this.state.dataToDownload}
filename="data.csv"
className="hidden"
ref={(r) => (this.csvLink = r)}
target="_blank"
/>
</div>
<div>
<ReactTable
ref={(r) => (this.reactTable = r)}
data={this.state.tableproperties.allData}
columns={columns}
filterable
defaultFilterMethod={(filter, row) =>
String(row[filter.id])
.toLowerCase()
.includes(filter.value.toLowerCase())
}
/>
</div>
</div>
);
}
}
export default AllPostPage;
This will work with filters as well.
I have it implemented like this in React + Typescript (no dependency):
/**
* #desc get table data as json
* #param data
* #param columns
*/
const getTableDataForExport = (data: any[], columns: any[]) => data?.map((record: any) => columns
.reduce((recordToDownload, column) => (
{ ...recordToDownload, [column.Header]: record[column.accessor] }
), {}));
/**
* #desc make csv from given data
* #param rows
* #param filename
*/
const makeCsv = async (rows: any[], filename: string) => {
const separator: string = ';';
const keys: string[] = Object.keys(rows[0]);
const csvContent = `${keys.join(separator)}\n${
rows.map((row) => keys.map((k) => {
let cell = row[k] === null || row[k] === undefined ? '' : row[k];
cell = cell instanceof Date
? cell.toLocaleString()
: cell.toString().replace(/"/g, '""');
if (cell.search(/("|,|\n)/g) >= 0) {
cell = `"${cell}"`;
}
return cell;
}).join(separator)).join('\n')}`;
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
if (navigator.msSaveBlob) { // In case of IE 10+
navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement('a');
if (link.download !== undefined) {
// Browsers that support HTML5 download attribute
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
the table:
<Table data={data} columns={columns} />
and the button:
<button
type="button"
onClick={() => makeCsv(getTableDataForExport(data, columns), `${filename}.csv`)}
>
Download table data CSV
</button>
I thought I'd piggyback on best wishes' extremely valuable answer with a simplified download implementation.
export = e => {
const currentRecords = this.ReactTable.getResolvedState().sortedData;
this.setState({ dataToDownload: this.dataToDownload(currentRecords, columns) }, () =>
this.csvLink.link.click()
);
}
dataToDownload = (data, columns) =>
data.map(record =>
columns.reduce((recordToDownload, column) => {
recordToDownload[column.Header] = record[column.accessor];
return recordToDownload;
}, {})
);
I used this to allow multiple table exports in one component by adding additional export functions.

Categories

Resources