So I have a function addAlert
that will add a message to an array for display as a React Bootstrap alert. Most of these alerts are static text, but one of them includes an "undo the last action" link. I would like the function called by this "undo" link to clear the alert in addition to undoing the action. I have a clearAlert
function available, but it takes an alert id that I do not have access to at the point in code where I am creating the alert content.
function App(props: AppProps){
[...]
const [currentAlerts, setCurrentAlerts] = useState<Array<AppAlert>>([]);
[...]
function addAlert(msg: React.ReactNode, style: string, callback?: (id: string) => {}) {
console.log("add alert triggered", currentAlerts);
let id = uuidv4();
let newTimeout = setTimeout(clearAlert, timeoutMilliseconds, id);
let newAlert = {
id: id,
msg: msg,
style: style,
callback: callback,
timeout: newTimeout
} as AppAlert;
let test = [...currentAlerts, newAlert];
console.log("after add alert", test);
setCurrentAlerts(test);
}
function clearAlert(id: string){
console.log("clear alert triggered", currentAlerts);
let timeout = currentAlerts.find(t => t.id === id)?.timeout;
if(timeout){
clearTimeout(timeout);
}
let newCurrent = currentAlerts.filter(t => t.id != id);
console.log("after clear alert", newCurrent);
setCurrentAlerts(newCurrent);
}
[...]
return (
<>
<div className={ "app-container " (error !== undefined ? "err" : "") } >
{ selectedMode === "Current" &&
<CurrentItems {...currentItemsProps} />
}
{ selectedMode === "History" &&
<History {...historyProps } />
}
{ selectedMode === "Configure" &&
<Configure {...globalProps} />
}
</div>
<div className="footer-container">
{
currentAlerts.map(a => (
<Alert variant={a.style} dismissible transition={false} onClose={a.callback}>
{a.msg}
</Alert>
))
}
</div>
</>
);
}
export default App;
[...]
} else {
// marked successfully, give option to undo
props.global.addAlert(<>
<span>You did it! </span>
<Alert.Link onClick={ () => { onUnMarkItem(itemId); } }>Undo</Alert.Link>
</>, "success");
}
[...]
I can pass a callback function to addAlert
, but is it possible to dynamically find the Alert.Link inside the ReactNode and set the onClick function of it?
The most straightforward solution I can think of with my current knowledge is to generate the alert id outside of the addAlert
and pass it in as a parameter, but I would prefer not to do that if there's another way.
CodePudding user response:
One way to handle this would be to modify your addAlert
function so that the msg
can be a function of the generated id
.
In order to minimize rewriting your existing addAlert
function calls, we'll keep it so that it can also accept a ReactNode
.
Now our msg
property is either a ReactNode
or a function that creates a ReactNode
, so we need to check if it's a function and handle that accordingly.
function addAlert(
>> msg: React.ReactNode | ((id: string) => React.ReactNode),
style: string,
callback?: (id: string) => {}
) {
console.log("add alert triggered", currentAlerts);
let id = uuidv4();
let newTimeout = setTimeout(clearAlert, timeoutMilliseconds, id);
>> const message = typeof msg === 'function' ? msg(id) : msg;
let newAlert = {
id: id,
>> msg: message,
style: style,
callback: callback,
timeout: newTimeout
} as AppAlert;
let test = [...currentAlerts, newAlert];
console.log("after add alert", test);
setCurrentAlerts(test);
}
The three lines with >>
are the ones that I changed.
In your CurrentItems.tsx
file, you can now provide your msg
as a function of id
, like this:
>> props.global.addAlert((alertId) => (<>
<span>You did it! </span>
<Alert.Link
onClick={() => {
onUnMarkItem(itemId);
>> props.global.clearAlert(alertId);
}}
>
Undo
</Alert.Link>
</>), "success");