I already understand the concept of Forwarding Refs and react-router-dom. But in this implementation, I'm not sure how to use it correctly. I have a child component, where there is a function that set null in a useState. I want this function to be executed every time I click on the menu item that renders this child component. This menu is mounted in the com List and the Router in the App, as shown in the 3 files below. Precisely, I don't know where to put the useRef to execute the child function resetMyState, if it's in App.js or or AppBarAndDrawer.js and how to do it.
childComponent.js
...
const MeusAnuncios = forwardRef((props, ref) => {
const [myState, setMyState] = useState(null);
function resetMyState(){
setMyState(null)
}
async function chargeMyState() {
await
...
setMyState(values)
...
}
...
AppBarAndDrawer.js
...
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List>
{[
{ label: "Minha Conta", text: "minhaConta", icon: "person" },
{ label: "Novo Anúncio", text: "novoAnuncio", icon: "queue_play_next" },
{ label: "Meus Anúncios", text: "meusAnuncios", icon: "dvr" },
{ label: "Estatísticas", text: "estatisticas", icon: "line_style" },
{ label: "Faturamento", text: "faturamento", icon: "local_atm" },
{ label: "childComponent", text: "childComponent", icon: "notifications" },
].map(({ label, text, icon }, index) => (
<ListItem
component={RouterLink}
selected={pathname === `/${text}`}
to={`/${text}`}
button
key={text}
disabled={text !=='minhaConta' && !cadCompleto ? true : false}
onClick={() => {click(text) }}
>
<ListItemIcon>
<Icon>{icon}</Icon>
</ListItemIcon>
<ListItemText primary={label.toUpperCase()} />
</ListItem>
))}
</List>
<Divider />
</div>
);
return(
...
{drawer}
...
)
...
App.js
...
export default function App() {
const childRef = useRef();
...
<Router>
<AppBarAndDrawer/>
<Switch>
<Route path="/childComponent">
<childComponent />
</Route>
...
...
CodePudding user response:
The ref
you create does need to reside in a common ancestor, i.e. App
, so it and a callback can be passed on to children components. The ref
to ChildComponent
and the callback to the AppBarAndDrawer
. Additionally, the ChildComponent
will need to use the useImperativeHandle hook to expose out the child's resetMyState
handler.
MeusAnuncios
Use the useImperativeHandle
hook to expose out the resetMyState
handler.
const MeusAnuncios = forwardRef((props, ref) => {
const [myState, setMyState] = useState(null);
function resetMyState(){
setMyState(null);
}
useImperativeHandle(ref, () => ({
resetMyState,
}));
async function chargeMyState() {
await
...
setMyState(values)
...
}
...
});
App
Create a resetChildState
callback and pass the ref to the child component and callback to the AppBarAndDrawer
component.
export default function App() {
const childRef = useRef();
const resetChildState = () => {
if (childRef.current.resetMyState) {
childRef.current.resetMyState();
}
};
...
<Router>
<AppBarAndDrawer onClick={resetChildState} /> // <-- pass callback
<Switch>
<Route path="/childComponent">
<ChildComponent ref={childRef} /> // <-- pass ref
</Route>
...
</Switch>
...
</Router>
}
AppBarAndDrawer
Consume and call the passed callback.
const AppBarAndDrawer = ({ onClick }) => { // <-- destructure callback
...
const drawer = (
<div>
...
<List>
{[
...
].map(({ label, text, icon }, index) => (
<ListItem
component={RouterLink}
selected={pathname === `/${text}`}
to={`/${text}`}
button
key={text}
disabled={text !=='minhaConta' && !cadCompleto}
onClick={() => {
click(text);
onClick(); // <-- call callback here
}}
>
<ListItemIcon>
<Icon>{icon}</Icon>
</ListItemIcon>
<ListItemText primary={label.toUpperCase()} />
</ListItem>
))}
</List>
...
</div>
);
...
};