Invalid Hook Call useSelector() with react-router - javascript

I have my state in Redux working for a shopping cart. Let's say I have two pages and want to pass the state from the shopping page to the cart/checkout page.
I am calling useSelector from this page but I get an error about an invalid Hook Call.
const CartPage = () => {
const selectedProducts = useSelector(
(state) => (state && state.products) || []
);
return (
<PageContainer>
This is the Cart Page
</PageContainer>);
};
export default CartPage;
And this component is rendered by react-dom-router like this
<AppContainer>
<Navigation />
<Switch>
<Route exact path="/" render={WelcomePage} />
<Route path="/cart" render={CartPage} />
<Route path="/shopping" render={ShoppingPage} />
</Switch>
</AppContainer>
I am storing the state from a component inside the page component ShoppingPage.
The useSelector hook is working only inside components outside the react-router-dom. Is there a way to get the state in one of these components?

EDIT: Somehow it worked using a callback inside the render attribute of Route
Here the fixed code
<AppContainer>
<Navigation />
<Switch>
<Route exact path="/" render={() => <WelcomePage /> } />
<Route path="/cart" render={() => <CartPage /> } />
<Route path="/shopping" render={() => <ShoppingPage /> } />
</Switch>
</AppContainer>

Related

Refresh component on route only in React-Router V5

Is there a way to refresh a route when using react router v5.
I have a onClick function which navigates to a route and refetches the data, however that is only if I use history.push('/') I want to refresh the component even if I am just on the same route '/' for example I am currently doing:
is there a better way to navigate programatically in react-router?
currently this handleChange method is in the header component.
const handleChange = (event: any) => {
setValue(event.target.value);
if (history.location.pathname === "/") {
alert('Changing');
// need to refresh component here?
}
history.push('/');
};
routing is:
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Header links={items} locations={Locations} />
<div className="App">
<Switch>
<Route exact path="/" component={home} />
<Route path="/details/:id" render={(props: RouteComponentProps<any>) => <Detail {...props} />} />
<Route component={NotFound} />
</Switch>
</div>
</BrowserRouter>
<ReactQueryDevtools />
</QueryClientProvider>

Trigger a rerender of parent component when a child component is rendered

I am using the following material-ui theme Paperbase and within the Header.js component, I have the following useEffect hook:
const [temperature, setTemperature] = useState([]);
const getTemperature= async () => {
try {
const response = await fetch('/get-temperature')
const tempData = await response.json();
setTemperature(tempData);
} catch (err) {
console.error(err.message);
}
};
useEffect(() => {
getTemperature();
}, []);
The main purpose of this, is to display the current temperature as info, within the header component, which gets displayed at first page load/render.
Now within my App.js below, I have the following return setup where the above Header component is called.
return (
<Router>
<UserProvider myinfo={myinfo}>
<Switch>
<Route path="/">
<ThemeProvider theme={theme}>
<div className={classes.root}>
<CssBaseline />
<nav className={classes.drawer}>
<Hidden xsDown implementation="css">
<Navigator />
</Hidden>
</nav>
<div className={classes.app}>
<Header
onDrawerToggle={handleDrawerToggle}
/>
<main className={classes.main}>
<Switch>
<Route exact path="/new-user"
render={(props) => <Content key={props.location.key} />}
/>
<Route exact path="/view-results"
render={(props) => <ViewResults key={props.location.key} />}
/>
</Switch>
</main>
</div>
</div>
</ThemeProvider>
</Route>
</Switch>
</UserProvider>
</Router>
);
My question is, how can I trigger a rerender of Header (parent) whenever the user routes to either /new-user or /view-results which in turn calls either Content.js or ViewResults.js, inorder to make the useEffect in Header.js refresh the data, from the REST api fetch and display the latest temperature in the header again?
Ideally anytime Content.js or ViewResults.js is rendered, ensure that Header.js getTemperature() is called.
Any help would be much appreciated.
Your current code is pretty close to a multi layout system. As being a component child of Route, you can access the current location via useLocation() or even the native window.location.pathname.
This is my example of multi layout React app. You can try to use it to adapt to your code.
The MainLayout use a fallback route when no path is specified. It also contains a Header and include a page
const Dispatcher = () => {
const history = useHistory();
history.push('/home');
return null;
};
const App = () => (
<BrowserRouter>
<Switch>
<Route
component={Dispatcher}
exact
path="/"
/>
<Route
exact
path="/login/:path?"
>
<LoginLayout>
<Switch>
<Route
component={LoginPage}
path="/login"
/>
</Switch>
</LoginLayout>
</Route>
<Route>
<MainLayout>
<Switch>
<Route
component={HomePage}
path="/home"
/>
</Switch>
</MainLayout>
</Route>
</Switch>
</BrowserRouter>
);
And here is the code for MainLayout
const MainLayout = ({ children }) => (
<Container
disableGutters
maxWidth={false}
>
<Header location={props.location} />
<Container
component="main"
maxWidth={false}
sx={styles.main}
>
{children}
</Container>
<Footer />
</Container>
);
Now that Header can be anything. You need to put a capture in this component
import { useLocation } from 'react-router-dom'
cont Header = (props) => {
const { pathname } = useLocation();
//alternatively you can access props.location
useEffect(() => {
if (pathname === '/new-user') {
getTemperature();
}
}, [pathname]);
};
Note that Header is not a direct descendant of Route therefore it cannot access the location directly via props. You need to transfer in chain
Route -> MainLayout -> Header
Or better use useLocation

render a component after some time with setTimeout ( ReactJs)

I want to render one component inside my App.Js after some time pass with setTimeout, there is any way to do that?
when I try, nothing happens...
my code:
function App() {
return (
<Router>
<GlobalStyle />
<SearchProvider>
<Header />
<Switch>
<Route exact path="/">
{setTimeout(() => {
return;
<>
<InitialLoad />
<Home />
</>;
}, 1200)}
</Route>
<Route exact path="/series">
<Series />
</Route>
<Route exact path="/movies">
<MoviesPage />
</Route>
<Route exact path="/popular">
<PopularPage />
</Route>
<Route exact path="/resultado-de-busca">
<SearchPage />
</Route>
</Switch>
</SearchProvider>
</Router>
);
}
want my InitialLoad and Home Components show after 1200 time, but nothing happens, how I fix it, please?
If you want to do that in the Route:
import { useState } from "react";
function App() {
const [isDisplayed, setIsDisplayed] = useState(false);
useEffect(() => {
setInterval(() => {
setIsDisplayed(true);
}, 1200);
}, []);
return (
<Router>
<GlobalStyle />
<SearchProvider>
<Header />
<Switch>
<Route exact path="/">
{isDisplayed &&
<>
<InitialLoad />
<Home />
</>;
}
</Route>
//the rest of your component
Route is to choose what component to render depends on your path (URL).
if you want to refresh your component, you need to change state value, or props value from parent.
try something like this:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [isHide, setIsHide] = useState(true);
setTimeout(() => setIsHide(false), 5000);
return (
<div className="App">
<h1>testing delay render</h1>
{!isHide ? <div>show after 5 seconds</div> : null}
</div>
);
}
Your best bet would probably be to use hooks in React. Essentially creating your components<InitialLoad /> <Home /> as usual in App.js but set display:none at the start, and then have your setTimeout() change the state and display your components.
This article may help
https://reactgo.com/settimeout-in-react-hooks/

React - can a component be styled depending on what other component is rendered?

I have a Search component, when the homepage component is rendered I'd like the Search component to be rendered at the bottom of the page. When any other page component is rendered I'd like the Search component to be at the top of the page.
Currently what I have my app.js as:
const App = () => {
return (
<BrowserRouter>
<Switch>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/work' component={Work} />
<Route path='/contact' component={Contact} />
</Switch>
</BrowserRouter>
)
}
and inside a page component:
const Contact = () => {
return (
<div>
<Search />
Contact
</div>
)
}
Obviously this way means I have to add the Search component to every component and choose whether I place it at the top or bottom.
My question is this, can I place it on the app.js like so:
const App = () => {
return (
<BrowserRouter>
<Search />
<Switch>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/work' component={Work} />
<Route path='/contact' component={Contact} />
</Switch>
</BrowserRouter>
)
}
And then depending on which page component is being rendered, style the Search component so it either appears at the top or bottom of the page.
Thanks
I would add a className prop to the Search component and add some if statement.
For example:
<Search className={location === '/' ? 'top' : 'bottom'} />
With the useLocation() hook provided by React Router, you can determine what page you're on.
const Contact () => {
const location = useLocation();
const styles = location === "something" ? {...topStyles} : {...downStyles};
return (
<div>
<Search style={styles} />
Contact
</div>
)
}

How can I render a component in React to 2 different components?

I'm working on a ReactJS project. I have 3 components in the main page "Index component" as follows,
Nav-
Featured-
Footer
My Nav component has 2 links to 2 different components.
My Switch is as follows,
<Switch>
<Route path="/home" component={props => <Index {...props} />} />
<Route path="/register" component={Register} />
<Route path="/login" component={Login} />
<Route path="/cart" component={Cart} />
<Redirect from="/" to="home" />
</Switch>
I also have my Index component as follows,
<React.Fragment>
<Search />
<Nav history={history} />
{this.homePageComponents()}
<Route
path="/home/bedding"
component={props => (
<Bedding beddingProducts={this.beddingProducts()} {...props} />
)}
/>
<Route
path="/home/bath"
component={props => (
<Bath bathProducts={this.bathProducts()} {...props} />
)}
/>
<Route path="/home/search" component={Search} />
</React.Fragment>
I'm trying to render the Nav component to both Bath and Bedding products but whenever I import it and use it there it gives me an Error saying this.props.history.replace is undefined.
This is the project's link.
https://github.com/MaxOffline/beetle
I think you can solve your issue with using "withRouter" wrapper component which react-router provide to wrap your Nav component.
https://reacttraining.com/react-router/web/api/withRouter
If you console.log(this.props) in the render method of nav.jsx you'll see that it contains a history object but that there is no replace method on that object.
You may be looking for the this.props.history.push method?
I found a very simple way to fix the problem.
All I needed to do is render components to the homepage conditionally so I simply added the following helper method to my "Index" Component.
homePageComponents = () => {
const featuredProducts = this.state.products.filter(
product => product.featured === true
);
if (this.props.history.location.pathname === "/home") {
return <Featured featuredProducts={featuredProducts} />;
}
};

Categories

Resources