Render components via window.location.pathname - javascript

I am trying to render different components via an if-statement, that depends on the path name. See return statement below.
return (
<>
<Navbar style={{position:"sticky"}} ammount={safedIds.length} setShowCalendar={setShowCalendar} />
<DateBar date={currentDate? currentDate:"no date available"} />
{eventsPrep.length > 0 ?
window.location.pathname === "/calendar"?
<Calendar />
:
<Home events={eventsPrep} addSafedId={addSafedId} />
: <></>}
</>
);
I know that the path updates but the components do not.
How do I fix this?

Related

Functions are not valid as react child Error while using Navigate in react router dom

I am getting the error ("Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.") when I am trying to use condition which includes Navigate to "/path" (look at the statement in the code).
return (
<>
<Router>
<Navbar bg="dark" variant="dark">
<Container>
<Navbar.Brand to="/">MoviesReviews</Navbar.Brand>
<Nav className="me-auto">
<Link to="/picks">Picks</Link>
<Link to="/critics">Critics</Link>
</Nav>
<Form className="d-flex">
<FormControl
type="search"
placeholder="Search"
className="me-2"
aria-label="Search"
id = "SearchBox"
/>
{redirect === true? (() => (<Navigate to = "/search"/>)) : null}
<Button variant="outline-success" onClick = {() => pressSearch()}>Search</Button>
</Form>
</Container>
</Navbar>
<Routes>
<Route path = "/" element = {<Reviews/>}/>
<Route exact path = "/picks" element = {<Reviews/>}/>
<Route exact path = "/critics" element = {<Critics/>}/>
<Route exact path = "/search" element = {<SearchReviews search = {searchString}/>}/>
</Routes>
</Router>
</>
);
If you look at the Route exact path for "/search", you see that I am actually returning a <Component/>. I am pretty sure the problem is in the line which contains the condition ({redirect === true? (() => (<Navigate to = "/search"/>)) : null}).
{redirect === true? (() => (<Navigate to = "/search"/>)) : null} with the above statement you :
in false case you are returning null.
in true case you are trying to render a function.
() => (<Navigate to = "/search"/>)
Hence it is not valid as a react child.
You have to do : {redirect === true ? <Navigate to="/search"/> : null} as mentioned in comments.
Functions are not valid JSX, they can't be rendered.
If you are conditionally rendering React components there are a couple syntaxes you can use, neither involves using a function. It's considered bad practice to compare boolean variables against true/false, just use the variable's value for the condition. If you just need to test the truthy/falsey-ness of a value then use !! to coerce it to a strict boolean value.
Using a ternary: condition ? <Component1 /> : null
{redirect ? <Navigate to="/search" replace /> : null}
This is useful if you need to conditionally render one of two different components, or to return null as a component result.
Using logical AND (&&): condition && <Component1 />
{redirect && <Navigate to="/search" replace />}
This is useful if you only need to conditionally render a single component.
If you are conditionally rendering the entire component render result then you should use the first in order to return null as valid JSX.
Suggestion
It would be better to issue an imperative redirect instead of setting any redirect state. This has the benefit of not requiring an additional React render cycle to effect the change.
In order for this component to use any react-router-dom hooks, the Router will need to be moved higher in the ReactTree, i.e. to the parent component or higher of this component.
Example:
import { useNavigate } from 'react-router-dom';
...
const Component = () => {
const navigate = useNavigate();
...
// logic to replace setting any `redirect` state
if (<condition to redirect>) {
navigate("/search", { replace: true });
}
...
return (
<>
<Navbar bg="dark" variant="dark">
<Container>
<Navbar.Brand to="/">MoviesReviews</Navbar.Brand>
<Nav className="me-auto">
<Link to="/picks">Picks</Link>
<Link to="/critics">Critics</Link>
</Nav>
<Form className="d-flex">
<FormControl
type="search"
placeholder="Search"
className="me-2"
aria-label="Search"
id = "SearchBox"
/>
<Button
variant="outline-success"
onClick={pressSearch}
>
Search
</Button>
</Form>
</Container>
</Navbar>
<Routes>
<Route path="/" element={<Reviews />} />
<Route path="/picks" element={<Reviews />} />
<Route path="/critics" element={<Critics />} />
<Route path="/search" element={<SearchReviews search={searchString} />} />
</Routes>
</>
);
};

React - Toggle theme from another component

I am working on a project on react but I have ran into an issue.
The issue I am having is that I need to be able to toggle my "dark" and "light" theme from a icon that is in a different component. This icon exists in my BottomNavigation but the function for switching the theme exists in my app.js.
Did some research on my own and found that I need to "lift the state up". Issue is that I need to lift it twice as my files look like this:
./Components
./Home.js
- (components gets added here)
./Navigation
./BottomNavigation.js
app.js
(/home is added here)
My app.js looks like:
function App() {
const [theme, setTheme] = useState("light");
const themeToggler = () => {
theme === "light" ? setTheme("dark") : setTheme("light");
};
return (
<ThemeProvider theme={theme === "light" ? lightTheme : darkTheme}>
<GlobalStyles />
<Router>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/account">
<Account />
</Route>
</Router>
</ThemeProvider>
);
}
My Home.js looks like:
const Home = (props) => {
const [showingState, setIsShowing] = useState(false);
return (
<div>
<TopNavigation isShowing={(showingState) => setIsShowing(showingState)} />
<BottomNavigation />
<ImageSlider />
<Grid />
{showingState && (
<CurrencyPopup
isShowing={(showingState) => setIsShowing(showingState)}
/>
)}
<BestSeller />
<CollectionPromo />
<Instagram />
<Footer />
</div>
);
};
My BottomNavigation.js looks like (only took the part with the icon):
<div className={classes.options_container}>
<IconApp className={classes.icon_container}>
<span className={classes.cart_sum}>$0.00</span>
<Cart className={classes.icon} />
</IconApp>
<IconApp className={classes.icon_container}>
<Heart className={classes.icon} />
</IconApp>
<IconApp className={classes.icon_container}>
<Visibility
onClick={() => props.setTheme("")} //This icon shall switch the theme
className={classes.icon}
/>
</IconApp>
<IconApp className={classes.icon_container}>
<a href="/account">
<User className={classes.icon} />
</a>
</IconApp>
</div>
If you have any ideas or need something more from the code, let me know!
For best practice, you have a few options:
Use React Context.
Use a state management library like Redux and MobX.
You definitely don't want to life state up in your case, because the two components are too far away from each other in the hierarchy.
Lifting state up would be solution that does not require any additional knowledge.
However, its excessive use (over three or more component) is a bad practice, since it makes code dirty
Using RecoilJS would be easiest, since its api is nearly identical to that of useState, and there is less work for you to do
Sticking to vanilla ReactJS, try React Context

Menubar is not refreshing after sign in or sign out in React

My Menubar is not automatically refreshing when I sign in or sign out, but when I refresh the page manually it is refreshing. I tried so many solutions, but still can't solve this.
Please help me to find a solution to this problem.
My code:
// index page
ReactDOM.render(<Provider store={store}>
<Routes />
</Provider>, document.getElementById('root'));
My Routes.js file
return (
<BrowserRouter>
<Menu /> //this is my menubar
<Switch>
<BuyerRoutes path = "/myprofile" exact component={ProfileUpdate} />
<BuyerRoutes path = "/dashboard" exact component={BuyerHome} />
<SellerRoutes path = "/seller" exact component={SellerHome} />
<SellerRoutes path = "/addproduct" exact component={AddProduct} />
</Switch>
</BrowserRouter>
);
my auth.js file
<Route
{...rest}
render = {props =>
isAuthenticate() && isAuthenticate().user.usertype == 1 ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname:"/login",
state: {from: props.location}
}}
/>
)
}
/>
My menu.js file
{user && user.usertype===0 ?
<Fragment>
<Link className="dropdown-item" to="/seller" >Home</Link>
<Link className="dropdown-item" to="/addproduct" >Add Product </Link>
</Fragment>:
<Fragment>
<Link className="dropdown-item" to="/myprofile" > My Profile</Link>
<Link className="dropdown-item" to="/dashboard" > My Orders</Link>
</Fragment>
}
Thanks in advance.
Your Menu component usage under <BrowserRouter> doesn't involve any dynamic props and state. In React, to re-render components dynamically, you should pass dynamic contents via props, whose changes will be reflected by re-rendering. That's why your <Menu> component is not changing at all while your router is changing.
You need to update the store state after sign in and sign out. For an example dispatch an action to delete user login state from the store.

Render multiple components with a single ternary operator

If currentProfiles.length > 0, I'd like to map over an array named profiles and render a profile component for each profile, and render a pagination component below the profiles. I tried this with a single ternary operator, but this results in only the pagination component being rendered.
{currentProfiles.length > 0 ? (
(currentProfiles.map(profile => (
<ProfileItem key={profile._id} profile={profile} />
)),
(
<Pagination
profilesPerPage={profilesPerPage}
totalProfiles={profiles.length}
/>
))
) : (
<Spinner />
)}
If I use two separate ternary operators, I get the list of profiles and pagination as expected, but can I do both things with a single conditional operator?
Your code just needs some restructuring. If you wrap the mapped profiles and pagination components in a parent fragment or other element, it's easy. Note, too, that the first example below still retains the ternary, as requested.
return (
<div className="App">
{currentProfiles.length ? (
<>
{currentProfiles.map(p => (
<Profile {...p} />
))}
<Pagination profilesPerPage={2} totalProfiles={totalProfiles} />
</>
) : (
<p>Loading...</p>
)}
</div>
);
However, you have a few options aside from wrapping them in a non-rendered Fragment or its shorthand derivative. You could also use an actual element, such as a div. Or even omit the parent entirely and place your logic within an array, as in:
<div className="App">
{currentProfiles.length ? [
currentProfiles.map(p => (
<Profile {...p} />
)),
<Pagination profilesPerPage={2} totalProfiles={totalProfiles} />
] : <p>Loading...</p>}
</div>
Always remember that, unless you utilize the second approach, you'll need to ensure siblings share a common parent.
Working example.
You can use an array or a fragment https://reactjs.org/docs/fragments.html
{currentProfiles.length > 0 ? (
<>
currentProfiles.map(profile => (
<ProfileItem key={profile._id} profile={profile} />
)
<Pagination
profilesPerPage={profilesPerPage}
totalProfiles={profiles.length}
/>
</>
) : (
<Spinner />
)}

Best way to render multiple child components

I just started learning ReactJS and am curious to know the best practice to rendering multiple different child components. What is the right way and most efficient way to do so? This is my current render function;
render() {
return (
<div>
<Display
image={this.state.professional.image}
username={this.state.professional.username}
/>
<div className="container">
<Navbar
brand_name={!!this.state.professional.brand_name}
username={!!this.state.professional.username}
description={!!this.state.professional.description}
menu={!!this.state.professional.menu}
reviews={!!this.state.reviews}
photos={!!this.state.professional.photos}
email={!!this.state.professional.email}
address={!!this.state.professional.service_addresses}
/>
<section className="artist-page">
<div className="container">
<About
description={this.state.professional.description}
accolades={this.state.professional.accolades}
cancellation={this.state.professional.lead_time_cancellation}
rules={this.state.professional.service_rules}
faqs={this.state.professional.faqs}
/>
{(this.state.professional.menu || this.state.professional.offers)
&&
<Services
services={this.state.professional.menu}
offers={this.state.offers}
/>}
{!!this.state.photos && <Portfolio photos={this.state.photos} />}
{!!this.state.reviews && <Reviews reviews={this.state.reviews} score={this.state.professional.rating_overall_stats} count={this.state.professional.review_count} />}
{(!!this.state.professional.email || !!this.state.professional.address) && <Contact
address={this.state.professional.service_addresses[0]}
name={!!this.state.professional.brand_name ? this.state.professional.brand_name : this.state.professional.username}
/>}
</div>
</section>
</div>
</div>
);
}
Think about what needs to be a state and what should be props.
It seems like this parent component that you pasted here is storing information of all the children as state, before passing down to its children as props.
Are you sure this parent needs to know anything about username, description, menu, etc of <Navbar>? If it doesn't, then save them as Navbar's own states.
You can pass down entire this.state.professional object as props.
<About professional = {this.state.professional}/>
Then in your About component, you can take apart the object and use it accordingly:
<p>{ this.props.professional.description }</p>
You can consider destructuring for readability
const {description, rules} = this.state.professional
return (
<About description={description} rules={rules}/>
)
Fix your <Navbar>
Personally I think that it looks weird and unreadable when you pass down boolean like that. You can consider this:
<Navbar reviews={this.state.reviews} {...this.state.professional} />
Then do your boolean evaluation inside Navbar component itself, here's a few example:
const {username, description, reviews} = this.props
<p>{ reviews || 'No reviews' }</p>
<p>{ username ? formatName(username) : "" }</p>
<p>{ description && 'Has description!' }</p>

Categories