new to React here, I want to have a filter (in Nav.js) that filters content on App.js, but is nested in a Layout component. What's the best way to pass around props here? Can I keep everything as a functional component?
files here:
https://codesandbox.io/s/filter-menu-react-layout-uvppj?file=/src/Layout.js
Just pass the setFilter as props and you should be good to go.
const Layout = props => {
const { setFilter, children } = props;
return (
<div>
<Nav setFilter={setFilter} />
{children}
</div>
);
};
Related
Many React components allow passing custom components as props, in order to customize certain parts of the UI. A popular example would be react-select, which allows to specify custom replacements for all its individual sub components.
In this example, I'm using a modal dialog component that allows specifying custom footer component in its components prop:
const Footer = () => <button>Close</button>;
const MyModal = ({ onClose, closeLabel }) => <Modal components={{ footer: Footer }}/>;
I would like to pass certain data down to the custom component. In this particular example, I would like to pass down the onClose and closeLabel props to the Footer component. What I could do is to declare the Footer component inline:
const MyModal = ({ onClose, closeLabel }) => (
<Modal
components={{ footer: () => <button onClick={onClose}>{closeLabel}</button> }}
/>
);
The problem with this approach is that every time MyModal is rendered, a new footer component is created, which causes React to completely recreate the footer DOM. In this simple example that wouldn't be a big problem, but in more complex scenarios it would make things slow and also cause the component to lose its state. Wrapping the footer component in useCallback() would partially solve the problem, but only as long as none of the values passed down into the footer component change.
I could use context to pass down the value, but that seems like a really complicated solution for a really simple problem. Are there any alternatives?
you can do something similar like this:
export default function App() {
return (
<MyModal>
<Footer /> // this will get the new props
</MyModal>
);
}
const Footer = ({ onClose, closeLabel }) => (
<button onClick={onClose}>{closeLabel}</button>
);
const MyModal = ({
onClose,
closeLabel,
children // here any child you pass to this component will have both onClose and closeLabel as props
}) => {
return (
<div>
{React.Children.map(children, (child) => {
return React.cloneElement(child, {
onClose,
closeLabel
});
})}
</div>
);
};
but anyways there is something not clear there in your code, if you are getting the onClose and closeLabel as props inside MyModal why just don't pass them to the Footer from where are you getting them? and if you don't have control over Modal component then you can't do anything else than giving it inline
I'm using React JS and Stripe JS to render a reusable payment form.
This is working fine except for one case which requires a button to be rendered outside of the component.
I was hoping to use a ref for the custom payment form but, because of the way Stripe requires the component to be injected and wrapped with an ElementsConsumer component, the underlying component class that is rendered isn't accessible so the 'ref' property cannot be used.
In the below example, would there be a way for me to allow a ref to be used for the '_PaymentForm' class when the 'PaymentForm' function is used to render it?
Thanks!
class _PaymentForm extends React.Component<PaymentFormProps, PaymentFormState> {
...
}
const InjectedPaymentForm = (props: PaymentFormProps) => {
return (
<ElementsConsumer>
{({ elements, stripe }) => (
<_PaymentForm {...props} elements={elements} stripe={stripe} />
)}
</ElementsConsumer>
);
}
export const PaymentForm = (props: PaymentFormProps) => {
const stripePromise = loadStripe(props.stripeApiKey);
return (
<Elements stripe={stripePromise}>
<InjectedPaymentForm {...props} />
</Elements>
);
}
export default PaymentForm;
I am trying to create a custom email input component (for a form) that wraps a Material-UI TextField component inside a custom gridding component that I made. Ideally, I would like to be able to pass any TextField prop I want into this component and have it applied to the inner TextField component by spreading the props, but I also would like to be able to pass any props for the custom gridding component and apply them to the grid component also via spreading.
Example (where variant is a TextField prop and width is a CustomGrid prop):
// CustomEmailField.tsx
...
export const CustomEmailField: React.FC<TextFieldProps & CustomGridProps> = (props) => {
return(
<CustomGrid {...props as CustomGridProps}>
<TextField {...props as TextFieldProps} />
</CustomGrid>
);
};
// index.tsx
...
const App = () => {
return(
<>
<h1>Enter your email</h1>
<CustomEmailField variant={'outlined'} width={2} />
</>
);
};
However, when spreading the props for the gridding component, I get an error message saying that the TextField props (variant in this example) do not exist for this gridding component, and likewise that the gridding component's props (width in this example) don't exist for the TextField component.
What would be a good way to solve this issue so that I can still have flexibility over the props I pass in to each (child) component without having to hardcode what props can be accepted by the email (parent) component?
Just create a new props type.
export type CustomEmailFieldProps = {
textField: TextFieldProps;
customGrid: CustomGridProps;
}
export const CustomEmailField: React.FC<CustomEmailFieldProps> = ({textField, customGrid}) => {
return(
<CustomGrid {...customGrid}>
<TextField {...textField} />
</CustomGrid>
);
};
To use just create an object of the props you want to pass to each.
// index.tsx
...
const App = () => {
return(
<>
<h1>Enter your email</h1>
<CustomEmailField textField={{variant: 'outlined'}} customGrid={{width: 2}} />
</>
);
};
I have to call a functional component from another functional component So how can I call child functional component from a functional component in react js.
import React from "react";
import TestFunctional from "./TestFucntional";
const TestListing = props => {
const { classes, theme } = props;
const handleClickTestOpen = () => {
return <TestFunctional />;
};
return (
<div>
<EditIcon
className={classes.icon}
onClick={handleClickTestOpen}
/>
</div>
);
};
export default TestListing;
I am trying to call or render TestFucntional component on EditIcon clicked but it is not called. So How can I call component?
Thanks.
You just use it in your jsx as a component as usual. You can see here
const ItemComponent = ({item}) => (
<li>{item.name}</li>)
const Component1 = ({list}) => (
<div>
MainComponent
<ul>
{list && list.map(item =><ItemComponent item={item} key={item.id}/>)}
</ul>
</div>)
const list = [{ id: 1, name: 'aaa'}, { id: 2, name: 'bbb'}]
ReactDOM.render(
<Component1 list={list}/>
, document.querySelector('.container')
);
From the above conversation, I guess you want conditional rendering, i.e. after any event you want to render the child component. To do so in the parent component, it should maintain a state. If you want to use functional parent component, you can use hooks. Or you can use some prop for the conditional rendering as well. Please provide a code snippet.
This is for reference: https://reactjs.org/docs/conditional-rendering.html
I may be over thinking this, but I am curious if importing a child component directly is bad practice with regards to coupling and testing.
Below is a simple example:
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
<Header></Header>
<div>{this.props.importantContent}</div>
</div>
)
}
}
To me it looks like there is now coupling between Widget and Header. With regards to testing, I don't see an easy way to mock the Header component when testing the Widget component.
How do other larger React apps handle cases like this? Should I pass Header in as a prop? If using react-redux, I can inject header with the Connect method like below to reduce boilerplate. Is that sound?
import { connect } from 'react-redux';
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
{this.props.header}
<div>{this.props.importantContent}</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
header: Header
}
}
export default connect(mapStateToProps)(Widget)
I am interested is simple doing what the community is generally doing. I see that one solution is doing shallow rendering to test on the main part of the component and not the child components using something like Enzyme.
Thoughts or other ideas?
Passing elements / components as props is a good idea. Having default props is a good idea too:
const Widget = ({
header = <div>Default Header.. </div>,
content = <div>Default Content.. </div>
}) =>
<div>
{header}
{content}
</div>
Then elsewhere in your app:
<Widget header={<Header title="Foo" />} content="content from props" />
No need to inject using connect
You can also pass a component, not just an element if you want to interact with props / send data back to parent:
const Widget = ({
Header = props => <div>Default Header.. </div>,
Content = props => <div>Default Content.. </div>
}) =>
<div>
<Header />
<Content />
</div>
Elsewhere:
<Widget Header={Header} Content={props => <Content />} />
As long as the component always renders the same thing it can be directly rendered as a child rather than the parent.
If all other portions of the Component remain constant and only the Header can be different across pages then you could actually implement it as an HOC instead of passing it as a props
const MyCompFactory = ({CustomHeader = DefaultHeader}) => {
return class Widget extends React.Component {
render() {
return (
<div>
<CustomHeader/>
<div>{this.props.importantContent}</div>
</div>
)
}
}
}
and use it like
const CustomComponent = MyCompFactory({CustomComponent: Header})
as long as testing is concerned in your case, you could just shallow render your component and then Search if the Header component is rendered something like
import Header from 'path/to/header'
const component = shallow(
<Widget {...customProps}/>
)
test('test' , () => {
expect(component.find(Header).exists()).toBe(true)
})