I need to pass a wrapper to the component Test. This wrapper could be anything and it needs to have its own props (function, bool, string, whatever).
function App() {
return (
<div className="App">
<h1>Yo</h1>
<Test Wrapper={<CustomWrapper text={"yes"} />} />
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
The component Test receives it as a prop and renders it including the children (here 'Nope')
function Test({ Wrapper }) {
return (
<div>
<Wrapper>
<div>Nope</div>
</Wrapper>
</div>
);
}
What is the right pattern to do that?
Pass the child as a prop would work
import React from "react";
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Yo</h1>
<Test Wrapper={CustomWrapper({ text: "yes" })} />
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const Test = ({ Wrapper }) => {
return (
<div>
<Wrapper children={<div>Nope</div>} />
</div>
);
};
const CustomWrapper = props => ({ children }) => {
return (
<div>
{children}
{props.text}
</div>
);
};
Related
I've two navbar layout and ten pages in my next js project. Now I need to set navbar one for five pages and navbar two for rest of the pages with layout.js at once. How can I do this. If anyone help me about this, I'll be very thankful. Thanks in advance.
NavbarOne.js
const NavbarOne = () => {
return (
<div>
<h1>Navbar One</h1>
</div>
);
}
export default NavbarOne;
NavbarTwo.js
const NavbarTwo = () => {
return (
<div>
<h1>Navbar Two</h1>
</div>
);
}
export default NavbarTwo;
Layout.js
import Footer from "../Footer";
import NavbarOne from "../Navbar/NavbarOne";
const Layout = ({ children }) => {
return (
<div>
<NavbarOne />
{/* <NavbarTwo /> */}
{children}
<Footer />
</div>
);
}
export default Layout;
_app.js
import Layout from '../components/layout/Layout';
import '../styles/styles.scss'
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
export default MyApp
Updated
Layout.js
import Footer from "../Footer";
import NavbarOne from "../Navbar/NavbarOne";
import NavbarTwo from "../Navbar/NavbarTwo";
const Layout = ({ children, criteria}) => {
return (
<div>
{ criteria ?
<NavbarOne />
:
<NavbarTwo />
}
{children}
<Footer />
</div>
);
}
export default Layout;
__app.js
import Layout from '../components/layout/Layout';
import '../styles/styles.scss'
function MyApp({ Component, pageProps }) {
return (
<Layout criteria={true}>
<Component {...pageProps} />
</Layout>
);
}
export default MyApp
__admission.js
import Layout from "../components/layout/Layout";
const admission = () => {
return (
<Layout criteria={false}>
<div>
<h1>Admission</h1>
</div>
</Layout>
);
}
export default admission;
You can do something like
const Layout = ({ children, criteria }) => {
return (
<div>
{ criteria ? //ternary operator
<NavbarOne />
:
<NavbarTwo />
}
{children}
<Footer />
</div>
);
}
export default Layout;
And pass a boolean to your Layout as criteria to choose between Navbars
const Page1 = () => {
return (
<Layout criteria={true}> // will display navbar 1
<p>Im page 1</p>
</Layout>
);
}
export default Layout;
const Page2 = () => {
return (
<Layout criteria={false}> // will display navbar 2
<p>Im page 2</p>
</Layout>
);
}
export default Layout;
With three navbars you can do
const Layout = ({ children, navbarType }) => {
return (
<div>
{ navbarType == 1 && <NavbarOne />}
{ navbarType == 2 && <NavbarTwo />}
{ navbarType == 3 && <NavbarThree />}
{children}
<Footer />
</div>
);
}
export default Layout;
And pass a number to your Layout as navbarType to choose between Navbars
const Page1 = () => {
return (
<Layout navbarType={1}> // will display navbar 1
<p>Im page 1</p>
</Layout>
);
}
export default Layout;
const Page2 = () => {
return (
<Layout navbarType={2}> // will display navbar 2
<p>Im page 2</p>
</Layout>
);
}
export default Layout;
const Page3 = () => {
return (
<Layout navbarType={3}> // will display navbar 3
<p>Im page 3</p>
</Layout>
);
}
export default Layout;
If you prefer an option where, you want to add N number of navbars.
and select them by some index number.
import Footer from "../Footer";
import NavbarOne from "../Navbar/NavbarOne";
const Layout = ({ children, index }) => {
const navBarList = [<NavbarOne />, <NavbarTwo />];
const renderNavBar = (_index) => navBarList[_index];
return (
<div>
{renderNavBar(index)}
{children}
<Footer />
</div>
);
}
export default Layout;
In your where you calling your app, you should be calling it as such.
import Layout from './components/layout.js';
export default function Home() {
return (
<div>
<Layout index={1}>
<p>byebye</p>
</Layout>
</div>
);
}
You can pass a props called navbar to the Layout Component like this:
export default function PageOne() {
return (
<Layout navbar="one">
<div className="container">
<h1>Page One</h1>
</div>
</Layout>
)
}
export default function PageTwo() {
return (
<Layout navbar="two">
<div className="container">
<h1>Page Two</h1>
</div>
</Layout>
)
}
And later render the Navbar Components conditionally like this:
const Layout = ({ children, navbar }) => {
return (
<div>
{ navbar === 'one' && <NavbarOne />}
{ navbar === 'two' && <NavbarTwo />}
{children}
<Footer />
</div>
);
}
i want to access props in the react functional component using react and typescript.
I have the MainComponent which has Layout component and i pass prop isOpen to Layout component from MainComponent like in below code,
const Layout: React.FC = ({children}) => ( //how to access isOpen prop here
<>
<leftNav />
{children}
<RightNav isOpen={isOpen} />
</>
);
interface Props {
item: item;
}
function Main({ item }: Props) {
return (
<Wrapper>
<Switch>
<Route
path="/items"
render={routeProps => (
<Layout isOpen={isOpen}> //passing prop here
<Items />
</Layout>
)}
/>
</Switch>
</Wrapper>
)
}
I have tried to access it like below
interface Props {
children: any;
isOpen: boolean;
}
const Layout: React.FC = ({children, isOpen}: Props) => (
<>
<leftNav />
{children}
<RightNav isOpen={isOpen} />
</>
);
But the above throws error jsxelement is not assignable to type FC component.
could someone help me fix this. thanks.
React.FC is generic and takes a type argument for the props. You can write your layout component like this.
interface Props {
isOpen: boolean;
// if you want isOpen props to be optional
// isOpen?: boolean;
}
const Layout: React.FC<Props> = ({children, isOpen}) => (
<>
<leftNav />
{children}
<RightNav isOpen={isOpen} />
</>
);
Your main component is fine.
You need to type the props for the FC generic:
//interface Props { ... }
const Layout: React.FC<Props> = ({children, isOpen}) => (
<>
<leftNav />
{children}
<RightNav isOpen={isOpen} />
</>
);
or omit the FC altogether:
//interface Props { ... }
const Layout: ({children, isOpen}: Props) => (
<>
<leftNav />
{children}
<RightNav isOpen={isOpen} />
</>
);
When I write this code I have an error:
React Hook "useRef" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function
What should I do with this code?
return ITEMS.map((item, i) => {
const elementRef = useRef(null);
return (
<div
ref={elementRef}
key={i}
>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && (
<Item
parentRef={elementRef}
/>
)}
</Wrapper>
</div>
);
}
Here are two possibilities, Either using useRef with an object/array, or using createRef as suggested by Yevgen Gorbunkov.
I'm not entirely sure as to the viability of these as the createRef option will create entirely new refs on each render, and the useRef option you'll need to make sure your keys/indexes are always the same.
const ITEMS = [{ name: "test" }, { name: "test2" }];
export default function App() {
const ref = useRef({});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{ITEMS.map((item, idx) => {
return (
<div key={idx} ref={element => (ref.current[idx] = element)}>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && (
<Item parentRef={ref.current[idx]} />
)}
</Wrapper>
</div>
);
})}
{ITEMS.map((item, idx) => {
const ref = createRef();
return (
<div key={idx} ref={ref}>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && <Item parentRef={ref} />}
</Wrapper>
</div>
);
})}
</div>
);
}
I want to create a functional component that wraps other components and uses JSX in its render method. I've seen a lot of content online about how to do this with class components, but I'm curious if it works with functional components.
function WrappedContent() {
return <p>I'm wrapped</p>
}
function Wrapper(WrappedComponent) {
return (
<div>
<p>Wrapped: </p>
<WrappedComponent />
</div>
)
};
function App() {
return (
<div>
<Wrapper>
<WrappedContent />
</Wrapper>
</div>
)
}
I'm guessing it has something to do with how child components are passed into <Wrapper> (via props.children?).
Here's a codesandbox with the above code: https://codesandbox.io/embed/gifted-cray-bqswi
(via props.children?)
Yes:
function WrappedContent() {
return <p>I'm wrapped</p>
}
function Wrapper(props) {
return (
<div>
<p>Wrapped: </p>
{props.children}
</div>
)
}
function App() {
return (
<div>
<Wrapper>
<WrappedContent />
</Wrapper>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Note that Wrapper accepts a parameter called props, and uses props.children. You could do it with destructuring if you don't have any other props (or even if you do, but it's a small number):
function Wrapper({children}) {
return (
<div>
<p>Wrapped: </p>
{children}
</div>
)
}
What gets passed to functional components is the props, and child elements are contained in props.children:
function WrappedContent() {
return <p>I'm wrapped</p>
}
function Wrapper(props) { // <-- here
return (
<div>
<p>Wrapped: </p>
{props.children} // <-- here
</div>
)
};
function App() {
return (
<div>
<Wrapper>
<WrappedContent />
</Wrapper>
</div>
)
}
https://codesandbox.io/embed/priceless-borg-brlbk
You can try something like this.
function WrappedContent() {
return <p>I'm wrapped</p>
}
function Wrapper(props) {
return (
<div>
<p>Wrapped: </p>
{props.children}
</div>
)
};
function App() {
return (
<div>
<Wrapper>
<WrappedContent />
</Wrapper>
</div>
)
}
You might also want to refer at this article https://reactjs.org/docs/composition-vs-inheritance.html
I can't figure out why:
I have react _ react-dom 16.8x
I have a component between my Provider and my Consumer to avoid cyclic call between the two aforementioned elements.
I would my context be accessible directly in lifecycle component and seen ReactJS propose the this.context's method to do that.
Here my sandbox
Here my reactjs snippet
import React, {Component}from "react";
import "./App.css";
// first we will make a new context
const MyContext = React.createContext();
// Then create a provider Component
class MyProvider extends Component {
state = {
name: 'Wes',
age: 100,
cool: true
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
growAYearOlder: () => this.setState({
age: this.state.age + 1
})
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
const Family = (props) => (
<div className="family">
<Person />
</div>
)
class Person extends Component {
componentDidMount(){
console.log("context: ", this.context)
}
render() {
console.log("context: ", this.context)
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
class App extends Component {
render() {
return (
<MyProvider>
<div>
<p>I am the app</p>
<Family />
</div>
</MyProvider>
);
}
}
export default App;
Why this.context is empty?
Any hint would be great,
thanks
You need to set contextType to consume a context.
On the react website it specifies as much anyway.
So the only change required:
class Person extends Component {
componentDidMount(){
console.log("context: ", this.context)
}
render() {
console.log("context: ", this.context)
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
Person.contextType = MyContext;
Hope this helps!
If you need access to MyContext in Person, you can wrap Person in the consumer and pass in context when you render it in Family:
const Family = (props) => (
<div className="family">
<MyContext.Consumer>
{(context) => {
<Person context={context} />
}}
</MyContext.Consumer>
</div>
)
class Person extends Component {
componentDidMount(){
console.log("context: ", this.props.context)
}
render() {
const { context } = this.props;
console.log("context: ", context)
return (
<div className="person">
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
</div>
)
}
}