Using Apollo GraphQL refetch method from outside - javascript

My render function
translateFileAndRefetchQuery = (urn) => {
translateFile(urn);
// How do I refetch data from here?
};
render() {
const translateFormatter = (cell, row) => {
return (
<span>
<Button
onClick={() => translateFileAndRefetchQuery(row.urn)}
>
Translate
</Button>
</span>
);
};
const columns = [
{
dataField: 'name',
text: 'Name',
},
{
dataField: 'lastUpdated',
text: 'Last updated',
},
{
dataField: 'lastTranslated',
text: 'Translate',
formatter: translateFormatter,
},
];
return (
<>
<h3>Models</h3>
<h4>Root > Plans</h4>
{this.state.plansFolderUrn != null &&
<Query query={getProjects()}>
{(data, refetch) => (
<>
<BootstrapTable
keyField="name"
data={data.projects}
columns={columns}
/>
</>
)}
</Query>
}
</>
);
}
I'm using the apollo data fetcher which have a refetch method. I need to call this from outside the Query.
How do i do this?

You could pass the function around in different callbacks. I added a decorator to translateFormatter to not call it right away :
translateFileAndRefetchQuery = (urn, refetch) => {
translateFile(urn);
refetch();
};
render() {
const translateFormatter = refetch => (cell, row) => {
return (
<span>
<Button
onClick={() => translateFileAndRefetchQuery(row.urn, refetch)}
>
Translate
</Button>
</span>
);
};
const getColumns = refetch => [
{
dataField: 'name',
text: 'Name',
},
{
dataField: 'lastUpdated',
text: 'Last updated',
},
{
dataField: 'lastTranslated',
text: 'Translate',
formatter: translateFormatter(refetch),
},
];
return (
<>
<h3>Models</h3>
<h4>Root > Plans</h4>
{this.state.plansFolderUrn != null &&
<Query query={getProjects()}>
{(data, refetch) => (
<>
<BootstrapTable
keyField="name"
data={data.projects}
columns={getColumns(refetch)}
/>
</>
)}
</Query>
}
</>
);
}

Related

Column of table from different API (React)

I have to fill the table with data which I have returned from different API : the first from smart contract and the second one from a MySQL table using axios.
Both of the sets of data returned are arrays and each elements with the same id I want to put it into a row.
I succeeded to put the data only from the smart contract.
Here's the code of my component :
function CandidaturesList() {
const [account, setAccounts] = useState(null);
const [contract, setContract] = useState(null);
const [candidatures, setCandidatures] = useState(null);
const [stateCandidatures, setStateCandidatures] = useState(null);
useEffect(() => {
async function fetchData() {
const web3 = await getWeb3();
const accounts = await web3.eth.getAccounts();
const networkId = await web3.eth.net.getId();
const deployedNetwork = OffreEmploiContract.networks[networkId];
const instance = new web3.eth.Contract(
OffreEmploiContract.abi,
deployedNetwork && deployedNetwork.address,
);
setAccounts(accounts);
setContract(instance);
const response = await instance.methods.getApply().call({ from: accounts[0] });
console.log(response);
setCandidatures(response);
axios.get('http://localhost:4000/api/getAll').then((res) => {
setStateCandidatures(res.data);
})
}
fetchData();
});
const project = [
{
title: "COMPANIES",
dataIndex: "nomCompagnie",
align: 'center',
render: text => (
<div className="avatar-info">
<Title level={5}>{text}</Title>
</div>
)
},
{
title: "JOB TITLE",
dataIndex: "titre_poste",
align: 'center',
render: text => (
<>
<div className="semibold">{text}</div>
</>)
},
{
title: "STATUS",
width: "15%",
dataIndex: "status",
align: 'center',
render: text => (
<>
<br>{text}</br>
<br></br>
<Steps current={1} progressDot={(dot, { status, index }) => (
<Popover
content={
<span>
step {index} status: {status}
</span>
}
>
{dot}
</Popover>
)} >
<Step title="Published" />
<Step title="Job interview" description="Mon, 18 Apr 2022" />
<Step title="Waiting for decision" />
<Step title="Completed" />
</Steps>,
</>)
},
{
title: "FINAL DECISION",
dataIndex: "decision",
align: 'center',
render: text => (
<>
{
text === "Valide" || text === "Refused" ?
<Tag color="cyan">{text}</Tag>
:
<Tag icon={<SyncOutlined spin />} color="processing">{text}</Tag>
}
</>
)
},
];
return (
<>
<Card
bordered={false}
className="criclebox tablespace mb-24"
title="Your Candidature"
extra={
<>
</>
}
>
<div className="table-responsive">
<Table
columns={project}
dataSource={candidatures }
pagination={false}
className="ant-border-space"
/>
</div>
</Card>
</>
);
}
export default CandidaturesList;
I must fill the Steps with the state stateCandidatures which is an array of objects , example :
{id: 1, date_entretien: 'Mon Apr 25 2022', status: 'waiting for interview'},
{id: 2, date_entretien: null, status: 'Published'}
and this is an example of the state candidatures :
[ id: '1', nomCompagnie:'Company1', titre_poste: 'manager', decision:'valide', …]
[ id: '2', nomCompagnie:'Company2', titre_poste: 'developer', decision:'processing', …]
and the second problem is i don't know how to fill the steps with those data ( the code shows a static steps)

Add custom add-button in material-table

Currently I have a simple material-table like this:
<MaterialTable
options={myOptions}
title="MyTitle"
columns={state.columns}
data={state.data}
tableRef={tableRef} // Not working
editable={{
onRowAdd: ...,
onRowDelete: ...,
onRowUpdate: ...
}}
/>;
where I'm trying to a create new add button (not edit the current one): each Row in the Bar Column should have a custom add button. I've looked through the MaterialTable source code but I couldn't reproduce the code that is used for the default add button which is:
calculatedProps.actions.push({
icon: calculatedProps.icons.Add,
tooltip: localization.addTooltip,
position: "toolbar",
disabled: !!this.dataManager.lastEditingRow,
onClick: () => {
this.dataManager.changeRowEditing();
this.setState({
...this.dataManager.getRenderState(),
showAddRow: !this.state.showAddRow,
});
},
});
in particular I can't get to access the dataManager variable.
That is how the current table looks like, and I need to add the add button where there is the red sign.
I think this is what you are looking for:
The Actions column represents the default actions set. I added an specific button using custom column rendering (docs):
//..previous columns definition
{
title: "Custom Add",
field: "internal_action",
editable: false,
render: (rowData) =>
rowData && (
<IconButton
color="secondary"
onClick={() => addActionRef.current.click()}
>
<AddIcon />
</IconButton>
)
}
*Using rowData as conditional, prevents from rendering while filling the addition row.
Then I triggered the add action as shown here:
const MyComponent() {
const addActionRef = React.useRef();
return (
<>
<button onClick={() => addActionRef.current.click()}>
Add new item
</button>
<MaterialTable
//...
components={{
Action: props => {
//If isn't the add action
if (typeof props.action === typeof Function || props.action.tooltip !== 'Add') {
return <MTableAction {...props} />
} else {
return <div ref={addActionRef} onClick={props.action.onClick}/>;
}}
}}
editable={{
onRowAdd: (newData, oldData) => Promise.resolve(); //your callback here
}}
/>
</>
);
}
I extended the original snippet in order to complete the addition cycle. If you need to handle different types of actions, I think Editable section from the oficial docs would be handy.
Hope this works for you! Full code and sandbox here:
import React, { Fragment, useState } from "react";
import MaterialTable, { MTableAction } from "material-table";
import AddIcon from "#material-ui/icons/AddAlarm";
import IconButton from "#material-ui/core/IconButton";
export default function CustomEditComponent(props) {
const tableRef = React.createRef();
const addActionRef = React.useRef();
const tableColumns = [
{ title: "Client", field: "client" },
{ title: "Name", field: "name" },
{ title: "Year", field: "year" },
{
title: "Custom Add",
field: "internal_action",
editable: false,
render: (rowData) =>
rowData && (
<IconButton
color="secondary"
onClick={() => addActionRef.current.click()}
>
<AddIcon />
</IconButton>
)
}
];
const [tableData, setTableData] = useState([
{
client: "client1",
name: "Mary",
year: "2019"
},
{
client: "client2",
name: "Yang",
year: "2018"
},
{
client: "client3",
name: "Kal",
year: "2019"
}
]);
return (
<Fragment>
<MaterialTable
tableRef={tableRef}
columns={tableColumns}
data={tableData}
title="Custom Add Mode"
options={{
search: false
}}
components={{
Action: (props) => {
//If isn't the add action
if (
typeof props.action === typeof Function ||
props.action.tooltip !== "Add"
) {
return <MTableAction {...props} />;
} else {
return <div ref={addActionRef} onClick={props.action.onClick} />;
}
}
}}
actions={[
{
icon: "save",
tooltip: "Save User",
onClick: (event, rowData) => alert("You saved " + rowData.name)
}
]}
editable={{
onRowAdd: (newData) =>
Promise.resolve(setTableData([...tableData, newData]))
}}
/>
</Fragment>
);

Simple React issue

Having trouble with a little snippet of js, I keep getting the error:
Failed to compile
./src/App.js
Line 81:9: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.
This error occurred during the build time and cannot be dismissed.
Code is as follows:
function TaskList() {
const deadlines = [
{ title: "wash the dishes!"},
{ title: "take out the trash!" },
{ title: "walk the dog!"},
];
return (
<div style={{ padding: "30px" }}>
{deadlines.map((task) => {
<>
<a>{task.title}</a>
</>
})}
</div>
);
}
You just need to return your JSX snippet in the map callback:
function TaskList() {
const deadlines = [
{ title: "wash the dishes!"},
{ title: "take out the trash!" },
{ title: "walk the dog!"},
];
return (
<div style={{ padding: "30px" }}>
{deadlines.map((task) => {
return (
<>
<a>{task.title}</a>
</>
);
})}
</div>
);
}
You need to return the jsx from inside the map method.
function TaskList() {
const deadlines = [
{ title: "wash the dishes!"},
{ title: "take out the trash!" },
{ title: "walk the dog!"},
];
return (
<div style={{ padding: "30px" }}>
{deadlines.map((task) => {
return ( // return the jsx
<>
<a>{task.title}</a>
</>
);
})}
</div>
);
}

Conditional rendering does not display my data properly

I have a component to display data on a material-ui table called UserRow.
This component is used on another component called users.
But in order to display the data properly in each field the only way I found was to create a conditional rendering, so i could just render the data that i wanted in each field, otherwise it would be duplicated.
Is there a way to just call once the <UserRow/> component and get the same results as i get in the image bellow?
UserRow:
export default function UserRow( props, {name, details}) {
const style = styles();
function UserName(props) {
const showUserRow = props.showUserRow;
if (showUserRow) {
return <ListItem>
<ListItemIcon >
<PeopleIcon className={style.iconColor}/>
</ListItemIcon>
<ListItemText className={style.text}>{props.userRow}</ListItemText>
</ListItem>
}
return<div></div>;
}
function IconDetails(props) {
const showDetailsRow = props.showDetailsRow;
if (showDetailsRow) {
return <Link to={`/users/${props.detailsRow}`}>
<ListItemIcon >
<ListAltIcon className={style.iconColor}/>
</ListItemIcon>
</Link>;
}
return<div></div>;
}
return (
<List>
<ListItem>
<UserName userRow={props.name} showUserRow={props.showUserRow}/>
<IconDetails detailsRow={props.details} showDetailsRow={props.showDetailsRow}/>
</ListItem>
</List>
)
}
users:
export default function User({ data }) {
const style = styles();
const userList = data.map((row) => {
return { name: row, details: row };
});
const [state] = React.useState({
users: [
...userList,
]
});
return (
<div>
<MaterialTable
icons={tableIcons}
title={<h1 className={style.title}>Users</h1>}
columns={[
{
title: "Name",
field: "name",
render: rowData => (
<UserRow showUserRow={true} showDetailsRow={false} name={rowData.name} />
)
},
{
title: "Details",
field: "details",
render: rowData => (
<UserRow showUserRow={false} showDetailsRow={true} details={rowData.details} />
)
},
]}
data={state.users}
options={{
search: true
}}
/>
</div>
)
}
What i had before:
UserRow:
export default function UserRow( props, {name, details}) {
const style = styles();
return (
<List>
<ListItem>
<ListItemIcon >
<PeopleIcon className={style.color}/>
</ListItemIcon>
<ListItemText className={style.text}>{name}</ListItemText>
<Link to={`/users/${details}`}>
<ListItemIcon >
<ListAltIcon className={style.iconColor}/>
</ListItemIcon>
</Link>
</ListItem>
</List>
)
}
users:
return (
<div>
<MaterialTable
icons={tableIcons}
title={<h1 className={style.title}>Users</h1>}
columns={[
{
title: "Name",
field: "name",
render: rowData => (
<UserRow name={rowData.name} details={rowData.details} />
)
},
{
title: "Details",
},
]}
data={state.users}
options={{
search: true
}}
/>
</div>
)
}
The problem here, in the previous solution, is that if we create a title Details, the material-ui table creates a div for the details and I can't place my icon there, and this would be a problem if i had more data and need to place the data in the respective position.
What i was trying to achieve with the previous solution was to cut down some code, because if i have many fields i will repeat too much code.
Link that might be useful: https://material-table.com/#/docs/get-started

How to show message when filtered list is empty in React

I am working on a project in which I am trying to show a div of content that says No results found for if the user types letters in the search input that do not match any filter in the list. I've tried using this similar solution as reference: React: How to show message when result is zero in react, but without success.
Here is a snippet of my code and one solution (of many) I have tried so far:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchQuery: ""
};
}
handleSearchQuery = event => {
this.setState({ searchQuery: event.target.value });
};
resetInputField = () => {
this.setState({ searchQuery: "" });
};
render() {
const { subContent, type, options, label } = this.props;
const { searchQuery } = this.state;
return (
<div
style={{
display: "grid",
alignItems: "center",
width: "100%",
margin: "0 0 24px 0",
fontSize: "14px"
}}
>
<div style={sx.rangeInputContainer}>
<input
style={sx.rangeInputLong}
type="text"
placeholder={placeholderText}
onChange={this.handleSearchQuery}
value={searchQuery}
/>
</div>
<div>
{options
.filter(
option =>
option.label
.toLowerCase()
.includes(searchQuery.toLowerCase()) || !searchQuery
)
.map((option, index) => {
return option.label.length !== 0 ? (
<div key={index} style={sx.filterOption}>
<SquareCheckbox
type="checkbox"
id={"multiSelectCheckbox-" + option.label}
/>
<label
style={{ color: "#FFF" }}
htmlFor={"multiSelectCheckbox-" + option.label}
>
{option.label}
</label>
</div>
) : (
<div
key={index}
style={{
display: "flex",
alignItems: "center",
marginTop: "16px"
}}
>
<img
style={{ width: "20px", cursor: "pointer" }}
src={resetIconSVG}
onClick={this.resetInputField}
/>
<div style={{ marginLeft: "16px" }}>
No results found for {searchQuery}
</div>
</div>
);
})}
</div>
</div>
);
}
}
Here's a snippet of options, which is in my parent component:
this.state = {
filters: [
{
label: 'Materials',
type: FILTER_TYPE.MULTI_SELECT,
expandedHandle: ()=> {
this.handleExpandedToggle('Materials'); },
options:materials,
expanded:false,
},
{
label: 'Status',
type: FILTER_TYPE.SELECT,
expandedHandle: ()=> { this.handleExpandedToggle('Status');
},
options: status,
expanded:false,
},
],
};
And the dummy .json data I am using:
export const materials = [
{ value: 'brass', label: 'brass' },
{ value: 'chrome', label: 'chrome' },
{ value: 'ceramic', label: 'ceramic' },
{ value: 'glass', label: 'glass' },
{ value: 'concrete', label: 'concrete' },
];
export const status = [
{ value: 'Show All', label: 'Show All' },
{ value: 'Enabled Only', label: 'Enabled Only' },
];
I've made an assumption about your options data, hopefully this helps (I simplified the codes)
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchQuery: ''
};
}
handleSearchQuery = event => {
this.setState({ searchQuery: event.target.value });
};
resetInputField = () => {
this.setState({ searchQuery: '' });
};
render() {
const { searchQuery } = this.state;
const options = [
{ label: 'react' },
{ label: 'angular' },
{ label: 'vue' }
];
const filteredOptions = options.filter(
option =>
option.label.toLowerCase().includes(searchQuery.toLowerCase()) ||
!searchQuery
);
return (
<div>
<div>
<input
type="text"
onChange={this.handleSearchQuery}
value={searchQuery}
/>
</div>
<div>
{filteredOptions.length > 0 ? (
filteredOptions.map((option, index) => {
return <div key={index}>{option.label}</div>;
})
) : (
<div>
No results found for {searchQuery}
</div>
)}
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Seems like your using a ternary operator inside of a return on your filter method. I would put the filter into a variable
const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchQuery.toLowerCase()) || !searchQuery).map((option, index) => {
return option.label.length !== 0 ? <div key={index} style={sx.filterOption}>
<SquareCheckbox type='checkbox' id={'multiSelectCheckbox-' + option.label} />
<label style={{ color: '#FFF' }} htmlFor={'multiSelectCheckbox-' + option.label}> {option.label} </label>
</div> })
and in your render use the ternary to check the length of the array
render {
return (
{filteredOptions.length > 0 ? filteredOptions : <div style = {{ marginLeft: '16px' }}>No results found for { searchQuery }</div>}
)
}

Categories

Resources