I have a Navbar that has buttons that change the language of the website. Instead of using different htmls, I'm just replacing the text with what I have in a database. However, doing anything in a component only updates that component and I need a way to update every other one
I haven't tried anything useful yet, since there's a complicated layout for usual solutions.
App.js
function App() {
return (
<div className="App">
<Router>
<div className="container" id="main">
<Navbar />
<Switch>
<Route exact path="/" component={Home}></Route>
<Route path="/gallery" component={Gallery}></Route>
</Switch>
</div>
</Router>
</div>
);
}
export default App;
My Navbar is outside of Switch that contains all the different pages I need to update.
All I need to do is to somehow pass an update request to every other component. Would be nice if it'd happen without reloading the page, though that would somewhat work.
You don't mention using Redux, so I assuming you are just using React state. Keep the language in the state of the top-level component, and pass the language to the components that need it. Give Switch a call-back function that changes the top-level state when the language is changed.
Related
I want to pass props to layout.js of Next.js 13. Here's what it'd look like:
// layout.js
export default Layout({children}) {
return (
<>
{/* I want to render different `text` depending on the page.js I'm rendering */}
<h1>{text}</h1>
{children}
</>
);
}
Unfortunately, I end up doing this:
// CustomLayout.js
export default Layout({children, text}) {
return (
<>
{/* I want to have different `text` depending on the Page.js I'm rendering */}
<h1>{text}</h1>
{children}
</>
);
}
My question is, is it possible to pass props to layouts in Next.js 13? Is there a better approach?
// In every page.js
import Layout from "somewhere/CustomLayout";
export default Page() {
return (
<Layout text="My text">
{/* My Page Content */}
</Layout>
);
}
No, you shouldn't pass in additional props to a layout.js file in nextjs 13 appdir.
As #yousoumar pointed out, you shouldn't be calling Layout yourself. It sits at the root (at least for all the components under it), so should be treated as such.
Your approach of creating a custom layout that passes text as a prop is a reasonable approach.
You shouldn't be trying to pass custom props to Layout, inside Next.js's new app directory, as this component is created by you, but Next.js itself would call it, by giving it two props:
children (required)
Layout components should accept and use a children prop. During rendering, children will be populated with the route segments the layout is wrapping. These will primarily be the component of a child Layout (if it exists) or Page, but could also be other special files like Loading or Error when applicable.
params (optional)
The dynamic route parameters object from the root segment down to that layout.
Wanting to pass custom props to it, I think, is because you wanna use it in different places, but the whole idea is to have one Layout by route segment. And subroutes inherit the layout of outer ones. For example, layout.js inside app/blog gets wrapped by layout.js inside app/.
Having Layout called inside page.js as you did, is like having any wrapper, but not a Layout as designed by Next.js with its specific abilities:
A layout is UI that is shared between multiple pages. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be nested.
import Layout from "somewhere/CustomLayout";
export default Page() {
return (
// ⚠️ this would behave like a normal wrapper, but not as a Layout as Next.js defines it
<Layout text="My text">
{/* My Page Content */}
</Layout>
);
}
I am trying to figure out how to configure my React router to load two overlaying components. I have a route /user/testUser/item/1. If item:id is in the url, an overlay component opens. However, I want the underlying component /user/testUser/ to load as well. When I close my overlay, that is, when I switch from /user/testUser/item/1 to /user/testUser/, the underlying user component is loaded initially. If the /user/testUser/item/1 overlay is open, the underlying user component is not loaded.
<Switch>
<PrivateRoute
exact
path="/user/:id*"
component={ProfileView}
/>
<PrivateRoute
exact
path="*/item/:id"
component={ShowItem}
/>
</Switch>
You can not render two routes at a time. You can manage it via optional params and render that component on a particular route.
Example :
<Switch>
<PrivateRoute
exact
path="/user/testUser/item/:id?"
component={ProfileView}
/>
</Switch>
const ProfileView = () => {
const { id } = useParams();
return (
<>
{id && <ShowItem />}
...
...
<OtherComponent />
</>
)
};
You can check conditionally id and based on that render the other components you want.
You can't really do it using routes, Switch selects only one Route's component to be rendered. You could probably experiment with having two Switch'es in parallel, but it sounds like the easiest way would be to handle displaying the overlay in the underlaying component.
I have the following code:
<View>
<Header />
<List />
</View>
If the user clicks on an edit button in a list item, react-router changes the page from /list to /list/{itemId}. This is a new page. After the user filled in some information and clicks on save, react-router changes back to /list. Now the full /list-page is rerendered!
Is there a way to cache a page, so react can detect the changes and rerender only them instead of the full page again?
It should also be possible to render the page directly (not over /list -> /list/{itemId}) so no modal solution I think.
I'm using react-router-redux v5.0.0-alpha.9 so the solution should be compatible with it. It should also be compatible with react-native and react-dom.
If you are working with react-router
Component can not be cached while going forward or back which lead to losing data and interaction while using Route
Component would be unmounted when Route was unmatched
After reading source code of Route we found that using children prop as a function could help to control rendering behavior.
Hiding instead of Removing would fix this issue.
I am already fixed it with my tools react-router-cache-route
Usage
Replace <Route> with <CacheRoute>
Replace <Switch> with <CacheSwitch>
If you want real <KeepAlive /> for React
I have my implementation react-activation
Online Demo
Usage
import KeepAlive, { AliveScope } from 'react-activation'
function App() {
const [show, setShow] = useState(true)
return (
<AliveScope>
<button onClick={() => setShow(show => !show)}>Toggle</button>
{show && (
<KeepAlive>
<Test />
</KeepAlive>
)}
</AliveScope>
)
}
The implementation principle is easy to say.
Because React will unload components that are in the intrinsic component hierarchy, we need to extract the components in <KeepAlive>, that is, their children props, and render them into a component that will not be unloaded.
Got into same problem recently and my initial nudge was to use something like react-router-cache-route or another package.
But main disadvantage of third-party packages is that they are usually slow to support new react-router versions and source code seemed to me overly complex for this problem.
So I researched further and stumbled on this suggestion from Dan Abramov and ended up with solution like this:
function App() {
const itemId = useParams<{ itemId: string }>();
return (
<Switch>
...
<Route
exact
path={['some-list-route', 'some-list-route/:itemId']}
>
<List style={itemId ? { display: 'none' } : undefined} />
{itemId && <ListItem />}
</Route>
...
</Switch>
)
}
We keep both list and item routes under single Route component to have control over their mounting behaviour. When we get item route — we hide our <List /> (but it stays mounted, "kept-alive") and conditionally mount <ListItem />.
We can also pass itemId to <List /> as a prop, if we need more control, like conditionally trigger data fetching only for list route.
I'm trying to port my project from using v3 of react-router to v4 of what is now called react-router-dom. Now the problems arise when I have a MenuBar component, which is completely separate from the routing logic (as you'd expect), because it will show the exact same links whatever the current path may be. Now that worked all nicely with v3, but now when I'm using NavLink, which has the same activeClassName property, the active route does not update on the NavBar, only on refresh. That seems a bit dumb, so there must be a way around this.
export default #inject('ui') #observer class App extends Component {
render() {
return (
<Router>
<div className={ styles.wrapper }>
<Sidebar />
<main className={ `${styles.main} ${!this.props.ui.menuOpen && styles.closed}` }>
<Route exact path="/" component={ HomePage } />
<Route path="/signup" component={ SignUpPage } />
<Route path="/login" component={ LoginPage } />
<Route path="/about" component={ AboutPage } />
</main>
<footer className="site-footer"></footer>
</div>
</Router>
);
}
}
The above is my main App logic and as you can see the routes are nested, but the Router itself wraps around the whole component.
What should I add to make them work again? (They do work properly on page refresh)
Based on your usage of the #observer decorator, it appears that you are using mobx-react. The thing to understand about observer is that it implements shouldComponentUpdate to optimize rendering performance. That sCU call will look at the current and next props, and if there is no difference, it will not re-render. This is a problem because by default, React Router uses the context to pass data and relies on elements re-rendering to get the updated values, but observer's sCU has no way to detect context changes.
The way that you can get around this is by passing the location object as a prop to the component that is wrapped by observer. Then, when the location changes, observer's shouldComponentUpdate will detect the difference and re-render.
You can see the blocked updates guide for more information.
I'm noobies in react / redux and I'm looking to create modal view following this 3 rules :
who is url dependant
who is generic
who is optimized
Cause of the url dependancy it's not optimized, it's rerender the parent.
Example: I have a view namespaces view at '/namespaces' who print all namespaces and when I open '/namespaces/edit' who open the modal view the namespaces is rerender. How to not rerender the namespaces list ?
Below the Router
<Route path="namespaces" component={NamespaceList}>
<Route path="edit" component={NamespaceEdit}/>
<Route path="create" component={NamespaceCreate}/>
</Route>
NamespacesList component
function NamespacesList({ push, children }) {
console.log("rendered !")
return (
<div>
NamespacesList
<p>
<Link to="/namespaces/create">Create</Link>
<br />
<Link to="/namespaces/edit">Edit</Link>
</p>
{children}
</div>
)
}
NamespacesCreate component (printed inside of the modal)
const NamespacesCreate = function() {
return (
<Modal>
NamespacesCreate
<p>
<Link to="/namespaces">Back to namespaces list ?</Link>
</p>
</Modal>
)
}
Use case :
I'm on the /templates/create (namespacelist is drawing in the back of the modal, in the console rendered ! is printed then when I click to link to comeback to the parent url /templates, rendered ! is printed again.
So there is a way to "optimize" it and not rerender the namespaceList or I need to choose between, or am I wrong ?
Don’t worry about how many times the component’s render() method is called. Rendering is very cheap in React, and if the content has not changed, it will not actually touch the DOM.
You should only start worrying about render() method calls when you start to experience real performance problems in your app which is very unlikely for one-off things like navigations. For example, you might want to optimize animations or form components that have ton of inputs.
If and when you have this problem (not earlier!), you can check out React guides to Advanced Performance optimizations, measuring wasted renders with ReactPerf, and learn about common performance anti-patterns.
Don’t let this complicate your code for no reason though. Only optimize when you have a real problem, and no sooner. React is very fast for most users’ needs out of the box, and render() method being called often is perfectly fine.