Passing code block as a prop to add to an array - reactjs

I have a very niche issue where I am trying to pass a block of executable code as a prop options. The prop looks like this...
columns={[
{
name: "Fund Name", //Title
width: "40%", //Colum Width
options: {[
var splitValue = value.split("//");
return (
<div className="fundName">{splitValue[0]}<p>{splitValue[1]}</p></div>
);
]}
}, {
name: "Review Date",
width: "20%"
}, {
name: "Company Debt",
width: "20%"
}, {
name: "Alerts",
width: "10%",
options: {[
return <Alerts {data: value} />
]}
}
}
So sometimes there are some variables and additional code and sometimes it may just be returning a component. Anyway. I need the code to look like this in the component...
const columns = [{
name: "Fund Name",
options: {
customBodyRender: (value, tableMeta, updateValue) => {
var splitValue = value.split("//");
return (
<div className="fundName">{splitValue[0]}<p>{splitValue[1]}</p></div>
);
}
}
}, {
name: "Review Date"
}, {
name: "Company Debt"
}, {
customBodyRender: (value, tableMeta, updateValue) => {
return <Alerts {data: value} />
}
}];
So firstly. Is this possible? can I pass variables like the splitValue, and, will it pick up that the passed in "value" variable is to be associated with the customBodyRender: (value,.... variable?
Here is my attempt but it is throwing a lot of errors. I feel I may be close if it even at all possible....
let columns = this.props.columns.map(item =>
item.options
? ({ ...item, options: { customBodyRender: (value, tableMeta, updateValue) => { eval(item.options) }} })
: item
);
Thanks

You can't use code directly in data structures because there is simply no syntax for this (in js generally) and ...
"The argument of the eval() function is a string."
Write code as strings? OK, you can. Problem here? JSX is transpiled into normal Javascript code (using bable). eval won't do that.
Is it possible to use JSX from running code with eval? Yes, you can using babel or other libs.
Of course using/storing functions is a preferred (safer) way for this kind of jobs.

Related

how can I put elements of an array in a checkbox?

I am using react hooks and have a dynamically filled array of values ​​after a backend call. How can I then insert these values ​​(which I don't know a priori) in a checkbox?
I get some properties from another component.
These properties change based on a selection from a table.
I would like to be able to add / remove these properties based on a selection on a checkbox when I open the "Available roles list"
const UsersList = (props) => {
const { loadedRoles } = props;
const [checked, setChecked] = useState([]);
const selectHandler = (Event, selected) => {
loadedRoles(selected.roles);
};
const handleChange = (Event) => {
setChecked({ ...props.roles, [Event.target.name]: Event.target.checked });
};
return (
<div>
<MaterialTable
title=" Users List"
data={props.users}
columns={[
{ title: "User Id", field: "id" },
{ title: "Active", field: "active" },
{ title: "System", field: "system" },
{ title: "Roles", field: "roles" },
]}
options={{
cellStyle: {
width: 100,
minWidth: 100,
height: 1,
},
headerStyle: {
width: 100,
minWidth: 100,
},
}}
onRowClick={selectHandler}
/>
<Card>Current Role: {props.current}</Card>
<Card>
<label>Available Roles: {props.roles}</label>
<Checkbox name={props.roles} onChange={handleChange} />
</Card>
</div>
I can assume that you receive from your backend an array of objects, containing the label, keys, and values for the checkboxes?
[
{ label: 'Checkbox #1', key: 'checkbox1', checked: true },
{ label: 'Checkbox #2', key: 'checkbox2', checked: false },
{ label: 'Checkbox #3', key: 'checkbox3', checked: true },
]
You can make the call to the API in a useEffect, store the result in a local state via useState, then iterate through the values in the rendering:
const [checkboxes, setCheckboxes] = useState(null)
useEffect(() => {
fetch('/your/api')
.then(res => res.json())
.then(checkboxes => setCheckboxes(checkboxes))
}, [])
return (
<ul>
{checkboxes === null
? <li>Loading...</li>
: checkboxes.map(checkbox => (
<li key={checkbox.key}>
<label>
<input type="checkbox" name={key} defaultChecked={checkbox.checked} />
{checkbox.label}
</label>
</li>
))
}
</ul>
)
The checkboxes here are not controlled (defaultChecked). To use controlled checkboxes instead, you’ll need to create another local state and initialize it from the values returned by the backend, and use checked & onChange props instead.

React MaterialTable clear all filters action - column and global filter

I am absolutely new to react.
It may trivial but I can't figure how to implement action that will clear all table filters.
In my table, I use date filter, drop-down, text, and global filters looking for one-click clear all filters
https://codesandbox.io/s/eager-thunder-ejlg5?file=/src/index.js
<MaterialTable
title="Free Action Preview"
columns={[
{ title: "Name", field: "name" },
{ title: "Surname", field: "surname" },
{ title: "Birth Year", field: "birthYear", type: "numeric" },
{
title: "Birth Place",
field: "birthCity",
lookup: { 34: "İstanbul", 63: "Şanlıurfa" }
}
]}
data={[
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{
name: "Zerya Betül",
surname: "Baran",
birthYear: 2017,
birthCity: 34
}
]}
actions={[
{
icon: () => <FilterNoneIcon />,
tooltip: "clear all filters",
isFreeAction: true,
onClick: (event) => alert("clear all filters logic")
}
]}
options={{
filtering: true,
sorting: true
}}
/>
As of this writing, it does not look like they have a clear filter functionality - according to this issue at least: https://github.com/mbrn/material-table/issues/1132 since they tagged it as wontfix - meaning they are not planning to work on it. However, on the same issue, 1 of the users recommended using a ref and manually accessing the table to filter the data (although that user later advised against it) - so you can try that as well.
Another way you could do this is to just remount the component. Since the component is remounted, it will begin at its initial state including unfiltered data
function App() {
const [muiTableKey, setMuiTableKey] = React.useState(0);
return (
<MaterialTable
key={muiTableKey}
actions={[
{
icon: () => <FilterNoneIcon />,
tooltip: "clear all filters",
isFreeAction: true,
onClick: (event) => {
setMuiTableKey(muiTableKey + 1); // set new key causing remount
}
}
]}

MUI-Datatables columns config conversion

I can't work out how to merge data differently based on what data is passed. Here is the prop data....
columns={[
{
name: "Fund Name", //Title
width: "40%", //Colum Width
options: {[
customBodyRender: (value, tableMeta, updateValue) => {
var splitValue = value.split("//");
return (
<div className="fundName">{splitValue[0]}<p>{splitValue[1]}</p></div>
);
}
]}
}, {
name: "Review Date",
width: "20%"
}, {
name: "Company Debt",
width: "20%"
}, {
name: "Alerts",
width: "10%",
options: {[
simpleBodyRender: <Options />
]}
}
}
So if it using customBodyRender I want it to do one thing and if it's doing simpleBodyRender I want it to do it slightly differently.
Here is the merging
let columns = this.props.columns.map(item => {
item.options
? ({ ...item, options: eval(item.options) })
: item
});
Basically I want it to look more like this...
let columns = this.props.columns.map(item => {
if(item.options == "customBodyRenderer"){
item.options
? ({ ...item, options: eval(item.options) })
: item
});
} else if(item.options == "simpleBodyRenderer"){
item.options
? ({ ...item, options: customBodyRender: (value, tableMeta, updateValue) => { eval(item.options) }})
: item
});
}
});
So if it's customBodyRenderer it prints everything but if it's simpleBodyRenderer it fills in the customBodyRender: (value, tableMeta, updateValue) => { for the user.
I hope that makes sense.
Thanks
Storing components in state and other structures is not a good idea at all. Doable but not recommended.
Just forget about eval ... you can only use functions that returns functions ... you can use components as parameters, render it inside other components, HOC, compose ... if you know how (more advanced react knowledge required) ... now KISS.
You can (?) use simple strings as source:
options: {
simpleBodyRender: "options"
}
... and transform it using '.map` into required format (function returning components):
let columns = this.props.columns.map(item => {
if(item.options && item.options.simpleBodyRender){
swith( item.options.simpleBodyRender ) {
case "options":
return { ...item,
options: {
customBodyRender: (value, tableMeta, updateValue) => <Options data={value} />
}
}
case "other options":
return { ...item,
options: {
customBodyRender: (value, tableMeta, updateValue) => <OtherOptions data={value} />
}
}
default: return { ...item,
options: {
customBodyRender: (value, tableMeta, updateValue) => <NotSupportedSimpleBodyRenderer />
}
}
}
}
}
}
Notece: options: { <<< single bracket, object, not {[
You can do it easily by modifying a bit your input data (columns), as follows :
const data = myInput.map((el) => { return { item: el }; });
Then you pass data as the input to your mui-datatable as follows :
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable data={data} columns={columns} options={options} />
</MuiThemeProvider>
In your column definition, 'name' is 'item'
So columns may look like this :
const columns = [
{
label: 'myLabel',
name: 'item',
options: {
display: true,
customBodyRender: (value) => {
return <p>{value.myData}</p>;
},
},
},
//other labels here
];

Convert JSX string to JSX expression with .map (React)

I need to change this data that is passed into a component
columns={[{
name: "Fund Name",
width: "40%"
}, {
name: "Review Date",
width: "20%"
}, {
name: "Company Debt",
width: "20%"
}, {
name: "Alerts",
width: "10%"
}, {
name: "Actions",
width: "10%",
options: 'customBodyRender: (value, tableMeta, updateValue) => { return (<Options />);'
}]
}
to this inside the component...
const columns = [{
name: "Fund Name",
}, {
name: "Review Date"
}, {
name: "Company Debt"
}, {
name: "Alerts",
}, {
name: "Actions",
options: {
customBodyRender: (value, tableMeta, updateValue) => {
return ( <Options /> );
}
}
}
}];
Basically I've got this far....
let columns = this.props.columns.map((item, key) =>
{ name: item.name }
);
This obviously isn't right but I'm not sure how to say that columns needs to be an array of the data in the .map function.
Please help.
Thanks
Use eval keyword to convert strings to JS expressions
let columns = this.props.columns.map(item =>
item.options
? ({ ...item, options: eval(item.options) })
: item
);
But there is one problem though. Babel cannot transpile the output of eval since it's generated in runtime. So you must convert JSX to normal JS expressions that doesn't need transpilation.
So you code must be like this
const columns = [{
name: "Actions",
options: {
customBodyRender: '(value, tableMeta, updateValue) => { return React.createElement(Options, null); }'
// or
customBodyRender: '(value, tableMeta, updateValue) => { return /*#__PURE__*/_react.default.createElement(Options, null); }'
// Depends on your babel configurations
}]

How to customize only one option from react-select?

I'm working with react-select and I want to customize only one option from the drop-down. Is there such an opportunity? I would like to do something like:
const CustomOption = ({ innerRef, innerProps, data }) => data.custom
? (<div ref={innerRef} {...innerProps} >I'm a custom link</div>)
: defaultOne //<--- here I would like to keep default option
<ReactSelect
components={{ Option: CustomOption }}
options={[
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
{ custom: true },
]}
/>
Any thoughts how to achive that?
Your feeling is good, you can achieve your goal with the following way:
const CustomOption = props => {
const { data, innerRef, innerProps } = props;
return data.custom ? (
<div ref={innerRef} {...innerProps}>
I'm a custom link
</div>
) : (
<components.Option {...props} />
);
};
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
{ custom: true }
];
function App() {
return <Select components={{ Option: CustomOption }} options={options} />;
}
The important thing to notice is to pass the entire props property to the components.Option to have the default behaviour.
Here a live example.

Resources