Passing portal location down to children - reactjs

I want to use React Portals (https://reactjs.org/docs/portals.html). The location where a portal should render is also rendered by React. There is a component that renders the portal "placeholder" (so to call it) and must pass it down to its children so that one (or many) of them can ReactDOM.createPortal.
Is there a recommended way for doing so?
I am using refs to capture the portal placeholder. On the first render the children get null in the prop they are expecting. I am also checking if it's null on the children before trying to render the portal. I am NOT storing the ref in the state. Sometimes it seems that acquiring the ref causes a prop change in the children, sometimes it just doesn't work.
If I try to document.getElementById directly on the children (by hardcoding the id of the placeholder in the parent component) it doesn't work because on the first render the DOM doesn't know about that element.
EDIT: Added code example
class Parent extends React.Component {
render {
return (
<div>
<div ref={ref => this.portalContainer = ref}/>
<SomeChild renderYourPortalHere={this.portalContainer}/>
</div>
)
}
}
const SomeChild = props => (
props.renderYourPortalHere
? ReactDOM.createPortal(<IrrelevantOtherComponent/>, props.renderYourPortalHere)
: null;
)

Related

React updating prop and rendering based on boolean value

I have a parent function component which has a boolean (which is set via child component and also used to render some container on parent component).
Is the below setup fine in terms of updating and dynamic rendering based on isSomeBoolean?
const [isSomeBoolean, setisSomeBoolean] = useState(true);
const updateIsSomeBoolean = (boolVal) => {
setisSomeBoolean(boolVal);
}
<ChildComp updateIsSomeBoolean={updateIsSomeBoolean} />
{isSomeBoolean && (
<div className="container">
....
</div>
)
}
In the child component, somewhere I invoke the parent function as below;
props.updateIsSomeBoolean(false);
Yes, Passing state and controller function to the child component is very normal in react.
But always keep in mind that changing state in parent component will render both components so keep the state near to component where is it required.
In your scenario, you're going in the right direction.

How to explicitly create an optional property in a React component when it is not specificied?

In a React component I need a ref to the HTML element I use for some manual processing (e.g. to listen to resize events). I can do that by defining a ref member in my component and set that in the render method:
render() {
return <div ref={this.myRef} />;
}
However, sometimes the owner of this component also needs this ref for other work (e.g. dynamic handling of certain other events). It makes sense to use the same ref here, so I added a property to the component:
render() {
const { innerRef } = this.props;
return <div ref={innerRef} />;
}
The problem is now that the parent component not always uses the inner ref, so it doesn't specify it when creating the inner component. In order to maintain the functionality of the inner component I want to explicitly create the ref if not given by the parent like:
public constructor(props: MyProps) {
super(props);
if (!props.innerRef) {
props.innerRef = React.createRef<HTMLElement>();
}
}
However, that's not accepted because props is read only. What other approach could I take to accomplish this explicit init, if not provided?
Note: I tried to use the static defaultProps to provide such a default, but defaultProps is used for all instances of the class, so they all share the same default ref then. Hence this is not a good approach.
Instead of trying to directly assign a new value to props, check if the innerRef is available from the props and if not pass an internally created ref to the div like this.
<div ref={innerRef || internallyCreatedRef} />

Adding a component to the render tree via an event handler, the component doesn't seem to receive new props. Why is this?

I have a context provider that I use to store a list of components. These components are rendered to a portal (they render absolutely positioned elements).
const A = ({children}) => {
// [{id: 1, component: () => <div>hi</>}, {}, etc ]
const [items, addItem] = useState([])
return (
<.Provider value={{items, addItem}}>
{children}
{items.map(item => createPortal(<Item />, topLevelDomNode))}
</.Provider>
)
}
Then, when I consume the context provider, I have a button that allows me to add components to the context provider state, which then renders those to the portal. This looks something like this:
const B = () => {
const {data, loading, error} = useMyRequestHook(...)
console.log('data is definitely updating!!', data) // i.e. props is definitely updating!
return (
<.Consumer>
{({addItem}) => (
<Button onClick={() => {
addItem({
id: 9,
// This component renders correctly, but DOESN'T update when data is updated
component: () => (
<SomeComponent
data={data}
/>
)
})
}}>
click to add component
</Button>
)}
</.Consumer>
)
}
Component B logs that the data is updating quite regularly. And when I click the button to add the component to the items list stored as state in the provider, it then renders as it should.
But the components in the items list don't re-render when the data property changes, even though these components receive the data property as props. I have tried using the class constructor with shouldComponentUpdate and the the component is clearly not receiving new props.
Why is this? Am I completely abusing react?
I think the reason is this.
Passing a component is not the same as rendering a component. By passing a component to a parent element, which then renders it, that component is used to render a child of the parent element and NOT the element where the component was defined.
Therefore it will never receive prop updates from where I expected it - where the component was defined. It will instead receive prop updates from where it is rendered (although the data variable is actually not coming from props in this case, which is another problem).
However, because of where it is defined. it IS forming a closure over the the props of where it is defined. That closure results in access to the data property.

Parent component re-renders cause duplicate children component renders

I have a component with URL like product/:id and it has some children components. When I route and change param id I get data from server again, set state, and re-render the page. But the children components are not re-rendered, they are rendered again ( So now I have twice same HTML Code ).
I don't know why it happened.
This is my children component:
listProductCardHTML = this.state.randomList.map((card, index) => {
return (
<ProductCard cardContent={card} key={index}>
</ProductCard>
)
})
And this is JSX:
<div className="box-product product-carousel" id="related-carousel">
{listProductCardHTML}
</div>
The problem seems that when you get routed to your product/:id, you push a new child to the randomList array.
Therefore the code listProductCardHTML = this.state.randomList.map((card, index) generates multiple JSX elements and are rendered.
Please check you setState method and check if randomList is being set correctly.

How to deal with the props and tightly coupled components in React?

I have a couple components which are tightly coupled to each other. The highest component receives prop called options. The prop options is passed down through next components and so on.
Which is the best way to emit changes from nested components to each others? I wouldn't rather use redux in this case.
This example will work for React16.3 and above.
Click here to check working example.
a) Get Data from parent component to nested chid conponent using context api of react
1. Grand Parent Component
Context lets us pass a value deep into the component tree without
explicitly threading it through every component. Create a context for
the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
const theme = "dark";
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
}
2. Parent Component
A component in the middle doesn't have to pass the theme down
explicitly anymore.
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
3. Child Component
function ThemedButton(props) {
// Use a Consumer to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
return (
<ThemeContext.Consumer>
{theme => <div>{theme}</div>}
</ThemeContext.Consumer>
);
}
Replace theme to options in your case.
For more detail take the reference of react doc. Click here
b) Store data from parent component to store and get it in nested child component using redux
Here you are getting data from state and passing options data to
your component
const mapStateToProps = (state) => ({
options: state.options,
});
Here you are connecting your component from state
export default connect(
mapStateToProps,
null,
)(ChildComponent);

Resources