I have the main layout that wraps whole app:
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Layout>
<Component {...pageProps} />
</Layout>
</>
);
}
I want to have the main layout and another layout to wrap, for example localhost:3000/subdirectory/... subdirectory.
You can do it simply like below:
First, remove the layout from your _app.js
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
</>
);
}
Second, create you first layout component:
const MainLayout = ({ children }) => {
return (
<>
<main>{children}</main>
</>
);
};
export default MainLayout;
Then Second layout:
const Dashboard = ({ children }) => {
return (
<>
<main>{children}</main>
</>
);
};
export default Dashboard ;
Then you can import your layout and use it like in your component:
import Dashboard from './Dashboard ';
export default function Card(){
return(
<Dashboard><div>your code</div><Dashboard>
)
}
And:
import MainLayoutfrom './MainLayout';
export default function Card(){
return(
<MainLayout><div>your code</div><MainLayout>
)
}
Related
I'm new to React, typescript and nextjs and I'm trying to pass multiple properties to a component. But, I seem to override them.
For example I have this in my _app.tsx
function App({ Component, myProps }: MyAppProps): JSX.Element {
const { ...props } = myProps;
return (
<MyStateProvider>
<Component {...props} />{' '}
</MyStateProvider>
);
}
In MyStateProvider, I have this in my-state-context.tsx
export const MyStateProvider: React.FC<any> = ( {children}) => {
const contextValue = doSomething();
return (
<MyStateContext.Provider value={contextValue}>
{children}
</MyStateContext.Provider>
);
};
However, I wish to something like this in my _app.tsx
function App({ Component, myProps }: MyAppProps): JSX.Element {
const { ...props } = myProps;
return (
<MyStateProvider {...props}>
<Component {...props} />{' '}
</MyStateProvider>
);
}
And this MyStateProvider in my-state-context.tsx
export const MyStateProvider: React.FC<any> = ( props, {children }) => {
const contextValue = doSomething(props); // <-- trying to accomplish passing props
return (
<MyStateContext.Provider value={contextValue}>
{children}
</MyStateContext.Provider>
);
};
What is the correct way to go about this?
You can do the following:
function App({ Component, ...myProps }: MyAppProps): JSX.Element {
return (
<MyStateProvider {...myProps}>
<Component {...myProps} />
</MyStateProvider>
);
}
and in your provider, supposing that you are passing the component like this <MyStateProvider Children={SomeComponent}/>
// note that first letter of Children should be uppercase to behave as a component
export const MyStateProvider: React.FC<any> = ({Children, ...props}) => {
const contextValue = doSomething({...props});
return (
<MyStateContext.Provider value={contextValue}>
<Children />
</MyStateContext.Provider>
);
};
See the example
Is having a global state [callable, setCallable] a good design to make pages listen to Layout events?
Example:
We can have a button Print in the AppBar of the layout MyLayout.
Then, each page calls setCallable({ print: () => alert('print me') }).
/* _app.tsx */
import "../styles/globals.css";
import type { AppProps } from "next/app";
import MyLayout from "../components/MyLayout";
import { useState } from "react";
function MyApp({ Component, pageProps }: AppProps) {
const [callable, setCallable] = useState();
return (
<MyLayout callable={callable}>
<Component {...pageProps} setCallable={setCallable} />
</MyLayout>
);
}
export default MyApp;
/* MyLayout.tsx */
export default function MyLayout({ children, callable }) {
return (
<>
<AppBar position="fixed" open={open}>
<Toolbar>
{callable?.print && (
<IconButton onClick={callable.print}>
<Print />
</IconButton>
)}
{/*Other buttons to call other callbacks can be added here*/}
</Toolbar>
</AppBar>
<main>{children}</main>
</>
);
}
/* MyPage.tsx */
export default function MyPage(props: any) {
useEffect(() => {
props.setCallable?.({
print: () => alert("print the current page"),
/* Other callbacks can be added here */
});
}, []);
}
I just see code _app.js file like this
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const { token } = parseCookies(ctx);
return { pageProps };
}
render() {
const { Component, pageProps } = this.props;
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
}
export default MyApp;
This is _app.js file
function App({ pageProps, Component }) {
return (
<Layout >
<Component {...pageProps} />
</Layout>
)
}
export default App;
I want to convert first code examle to this. How to do this?
You should be able to set it up as follows, but be aware when you have getInitialProps on your app you are blocking every page, see more details here: https://nextjs.org/docs/advanced-features/custom-app
function App({ pageProps, Component }) {
return (
<Layout >
<Component {...pageProps} />
</Layout>
)
}
App.getInitialProps = async (appContext) => {
// Logic goes here
}
export default App;
this seems like a duplicate question but it is not, the examples I have seen explain how to pass a function through props.
I have three components: ComponentA, ComponentB and App (App is the component root). I want to call a function contains in ComponentA using a button that is inside of ComponentB.
import "./styles.css";
import ComponentA from "./componentA";
import ComponentB from "./componentB";
export default function App() {
return (
<div className="App">
<>
<ComponentA>
<ComponentB />
</ComponentA>
</>
</div>
);
}
const ComponentA = ({ children }) => {
const hello = () => {
alert("hello");
//In my real scenario, this method contains a big logic...
};
return (
<>
Component A<div>{children}</div>
</>
);
};
const ComponentB = () => {
const callComponentAFunction = () => {
// I need call "hello()" from ComponentA
};
return (
<>
<button onClick={callComponentAFunction}>
Call function from ComponentA
</button>
</>
);
};
How can I call hello() (function inside of ComponentA) from ComponentB?
this is my live code
You can achieve that in many ways. Pass that function as props from component A.
Working example Modified code
//App.js
import "./styles.css";
import ComponentA from "./componentA";
// import ComponentB from "./componentB";
export default function App() {
return (
<div className="App">
<>
<ComponentA />
</>
</div>
);
}
//Component A
import ComponentB from "./componentB";
const ComponentA = ({ children }) => {
const hello = () => {
alert("hello");
};
return (
<>
Component A<div>{children}</div>
<ComponentB hello={hello} />
</>
);
};
export default ComponentA;
//Component B
const ComponentB = ({ hello }) => {
return (
<>
<button onClick={hello}>Call function from ComponentA</button>
</>
);
};
export default ComponentB;
you can also use the React.Children.map, the example like this: https://codesandbox.io/s/hardcore-ellis-ksgkd?file=/src/componentB.js
My page layout for a Gatsby site looks like this.
const Container = ({location, children, pageContext}) => {
return (
<>
<Header location={location} />
<Breadcrumbs pageContext={pageContext} />
{children}
<Footer />
</>
)
}
I need to pass location and pageContext from the page to the child components. I have tried to add location and pageContext to the DataProvider like this:
export const DataContext = React.createContext();
const DataProvider = ({ children, location, pageContext }) => {
return (
<DataContext.Provider value={{
location,
pageContext
}}>
{children}
</DataContext.Provider>
)
};
export default DataContext
export { DataProvider }
Then I use DataProvider in gatsby-ssr.js and gatsby-browser.js like this:
export const wrapRootElement = ({ element }) => (
<ThemeProvider theme={theme}>
<DataProvider>
{element}
</DataProvider>
</ThemeProvider>
);
In the child component:
const HeaderLinks = () => {
return (
<DataContext.Consumer>
{
context => (
<Menu
theme="light"
mode="horizontal"
selectedKeys={[context.location.pathname]}
>
<Menu.Item key={key}>
<Link to={url}>{name}</Link>
</Menu.Item>
</Menu>
)
}
</DataContext.Consumer>
)
}
But it doesn't seem to work, as it is not getting updated when I move to another page. (I also have wrapPageElement with Container, may be that's reasons.)
How can I pass location and pageContext to the child components? Is it better to use React Context or simply pass them as props? If I should use React Context, how can I correct my code to make it work?
Instead of using wrapRootElement to use ContexProvider you can make use of wrapPageElement where you can get the page props and pass them on to the DataProvider. This will make sure that pageContext and location change on each page
export const wrapRootElement = ({ element }) => (
<ThemeProvider theme={theme}>
{element}
</ThemeProvider>
);
export const wrapPageElement = ({ element, props }) => (
<DataProvider value={props}>
{element}
</DataProvider>
);
export const DataContext = React.createContext();
const DataProvider = ({ children, value }) => {
const {location, pageContext} = value;
return (
<DataContext.Provider value={{
location,
pageContext
}}>
{children}
</DataContext.Provider>
)
};
export default DataContext
export { DataProvider }
I ended up using useLocation from #reach/router to return location in child components. And I simply pass pageContext as a prop to <Breadcrumbs />, as it is used only once and is not passed down to any child components.