Wrapping props component in React js - javascript

I have prop 'drawerComponent' which goes in Drawer prop 'component'
This code works fine.
render() {
return(
<Drawer component={this.props.drawerComponent} />
)
}
But if I try to wrap drawerComponent by other one it fails. I get 'Element type error' or drawerComponent is not rendering
render() {
const Wrapper = (props) => <div>{this.props.drawerComponent}</div>
return(
<Drawer component={() => <Wrapper drawerComponent={this.props.drawerComponent} />} />
)
}
What's wrong with it?

You Drawer component accepts a component instance and not a component, you can simply write the wrapper like
render() {
const Wrapper = (props) => <div>{props.drawerComponent}</div>
return(
<Drawer component={<Wrapper drawerComponent={this.props.drawerComponent} />} />
}

Related

How to correctly pass a callback and a state to the Layout element in React Router Dom?

How to correctly pass callbacks and states to the Layout so that they can be used elsewhere? When I share this as below, I have errors and a white screen:
class Menu extends Component {
constructor(props) {
super(props);
this.onSearchF = this.onSearchF.bind(this)
}
state = {
searchBlock: false,
};
onSearchF = (keyword) => {
const filtered = this.state.data.filter((entry) =>
Object.values(entry).some(
(val) => typeof val === "string" && val.toLowerCase().includes(keyword.toLowerCase())
)
);
};
render() {
return (
<div className="content">
<Routes>
<Route path="/" element={<Layout searchBlock={this.state.searchBlock} onSearch={()=>this.onSearchF()}/>}>
<Route
index
element={
<Home data={this.state.data} num={this.state.data.length} />
}
/>
</Route>
</Routes>
</div>
);
}
}
export default Menu;
Here I pass the callback to the Header that I previously passed to the Layout:
const Layout = () => {
return (
<>
<Header sblock={this.props.searchBlock} onS = {this.props.onSearch}/>
</>
);
};
export default Layout;
I want to use the callback here:
function Header() {
return (
<header className="header">
<button onClick={()=>console.log(this.props.sblock)}>button</button>
</header>
);
}
export default Header;
Your Layout is a functional component, and you are trying to use this.props in it; this is incorrect. Get the props as part of arguments instead, like so:
import { Outlet } from "react-router-dom";
const Layout = ({searchBlock,onSearch}) => {
return (
<>
<Header sblock={searchBlock} onS={onSearch}/>
<Outlet/>
</>
);
};
export default Layout;
Issues
The Layout component isn't accepting any props.
The Layout component isn't rendering an Outlet for nested routes.
Solution
It seems that Layout only exists to render the Header component. I'd suggest rendering Header directly in the Main component.
Example:
class Menu extends Component {
state = {
data: [],
searchBlock: false,
};
onSearch = (keyword) => {
const filtered = this.state.data.filter((entry) =>
Object.values(entry).some((val) =>
typeof val === "string"
&& val.toLowerCase().includes(keyword.toLowerCase())
)
);
... do something with filtered ...
};
render() {
const { data, searchBlock } = this.state;
return (
<div className="content">
<Header sblock={searchBlock} onS={this.onSearch} />
<Routes>
<Route
path="/"
element={<Home data={data} num={data.length} />}
/>
</Routes>
</div>
);
}
}
export default Menu;

how do you pass down an object containing props

after getting all mixed up with state, i am now trying to restructure my app in a way that might be more reflective of best practices (not sure if this is the way, advice is welcome.)
so, i have my main page, which holds 3 states: viewer,buyside,sellside
there are also three different components, one for each of those states.
i want to be able to pass the props down from the main page, through those components, to their children (i've read this is the best approach??)
main page:
//we have 3 states for the website: viewer,buyside customer, sellside customer
const [visitorType, setVisitorType] = useState('viewer');
if (visitorType == 'viewer') {
return(
<div>
<Viewer visitortype='viewer' setvisitor={()=>setVisitorType()}/>
</div>
)}
else if (visitorType =='buyside') {
return(
<div>
<Buyside visitortype='buyside' setvisitor={()=>setVisitorType()}/>
</div>
)}
else if (visitorType =='sellside') {
return(
<div>
<Sellside visitortype='sellside' setvisitor={()=>setVisitorType()}/>
</div>
)}
};
what is the best way to pass down the main page props, so that i can bring them down to any grandchildren, along with the child props?
the viewer component -UPDATED-:
const MainView = (props) => {
return(
<>
<Navbar mainprops={{props}}/>
</>
)
};
export default MainView
i was previously just passing them individually, but realized it might be better to do so as one object...
UPDATE: point taken on the syntax, but i'm wondering how i can best pass the objects
nav component (grandchild)
const Navbar = (props) => {
const {mainprops} = props.mainprops;
if (mainprops.visitortype == 'viewer') {
return(
<>
<h1>viewer navbar</h1>
</>
)}
else if (mainprops.visitortype =='buyside') {
return(
<>
<h1>buyside navbar</h1>
</>
)}
else if (mainprops.visitortype =='sellside') {
return(
<>
<h1>sellside navbar</h1>
</>
)}
};
export default Navbar;
UPDATE 2 - this works, but not sure if it is the correct way, are these still considered object literals??
viewer component:
const MainView = (props) => {
const mainprops = {...props}
return(
<>
<Navbar mainprops={mainprops}/>
</>
)
};
export default MainView
navbar component
const Navbar = (props) => {
const mainprops = {...props.mainprops};
if (mainprops.visitortype == 'viewer') {
return(
<>
<h1>viewer navbar</h1>
</>
)}
else if (mainprops.visitortype =='buyside') {
return(
<>
<h1>buyside navbar</h1>
</>
)}
else if (mainprops.visitortype =='sellside') {
return(
<>
<h1>sellside navbar</h1>
</>
)}
};
export default Navbar;
if this is correct, then is this what #amir meant?
First there are certain rules for passing props:
You never ever pass literal object as props since it will not be the same every re-render and will cause the child component to re-render too (without any new info)
You don't need to do that
<Viewer visitortype='viewer' setvisitor={()=>setVisitorType()}/>
You can:
<Viewer visitortype='viewer' setvisitor={setVisitorType}/>
since it comes from useState react make sure the setVisitorType keeps the same reference
And now for you error, you almost correct you just did a js syntax error
you should write it like this:
const MainView = (props) => {
return(
<>
<Navbar mainobj={{
visitortype:props.visitortype,
setvisitor:props.setvisitor
}}
/>
</>
)
};
export default MainView
But again you never send literal object as props
I would keep it inside a ref or state (depend if the visitor state will be change)

How to access props in the component using react and typescript?

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} />
</>
);

Handle 'lambda in JSX attribute' case

There is an existing code:
const FooRoute: React.SFC<RouteProps> =
({ component: Component, ...rest }) => {
if (!auth.isFoo()) {
return <Redirect to={{ pathname: '/404' }} />;
}
if ('render' in rest) {
return (
<Route {...rest} render={rest.render} />
);
} else {
return (
Component
?
<Route
{...rest}
render={
(props: RouteComponentProps<{}>) => <Component {...props} />}
/>
: null
);
}
};
There is an error here:
Now how does the getComponent function will look like in this case?
Thought about smth like:
const getComponent = (props: RouteComponentProps<{}>) => () => (
<Component {...props} />
)
Then one can simply:
<Route
{...rest}
render={getComponent}
/>
But in this case Component is undefined. Any clues?
EDIT: Important Note - using TypeScript. So have to pass the Component somehow down into getComponent.
EDIT2: the reason I've used double lambda is because it allows handling situations like this:
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => (foo: string) => {
...
}
with
<button onClick={handleClick('bar')} ... // foo === 'bar'
In your case getComponent is a lambda function which returns another lambda function but not component because of (props) => () => .... Modify your getComponent function to receive Component from render function like below.
const getComponent = (Component: Component) => (props: RouteComponentProps<{}>) => (
<Component {...props} />
)
and modify this 'render' function too.
<Route
{...rest}
render={getComponent(Component)}
/>
Note: You are receiving component as component with small c and using it as Component with capital C.

react got error of unknown props

I want to try stateless component in react, I split them into 2, but I got error of Warning: Unknown propsitemson <renderItems> tag
What's wrong with my code below?
import Items from './Items'
import Infinite from 'react-infinite'
const LeftPanel = (props) => {
return(
<div className="leftPanel">
<renderItems {...props} />
</div>
)
}
const renderItems = (props) => (
<Infinite containerHeight={200} elementHeight={118}>
{map(props.items, (item, index) => (
<Items
index={index}
item={item}
/>
))}
</Infinite>
)
export default LeftPanel
I think you should rename your renderItems to RenderItems.
I was seeing this and it looks like in JSX you need to put capital letter on the first word of your component name:
<component /> compiles to React.createElement('component') (html tag)
<Component /> compiles to React.createElement(Component)
There's a syntactic error in your code. You're using parenthesis in your functional component instead of curly braces.
const renderItems = (props) => {
return (<Infinite containerHeight={200} elementHeight={118}>
{map(props.items, (item, index) => (
<Items
index={index}
item={item}
/>
))}
</Infinite>);
}
Furthermore, lowercase component names are now deprecated so renderItems should be called RenderItems.

Categories

Resources