Creating React components without rendering them - reactjs

I'm trying to create some components that need to communicate their loading states to the parent component.
Here's an example:
const Parent = () => {
// Loading
const [hasFirstFinishedLoading, setHasFirstFinishedLoading] = React.useState(false);
const [hasSecondFinishedLoading, setHasSecondFinishedLoading] = React.useState(false);
const children = (
<>
<First
onFinishedLoading={() => setHasFirstFinishedLoading(true)}
/>
<Second
onFinishedLoading={() => setHasSecondFinishedLoading(true)}
/>
</>
);
if (hasFirstFinishedLoading && hasSecondFinishedLoading) {
return <>{children}</>
}
return <LoadingComponent />
}
Essentially, the idea is that I render a Loading component while the children components are loading (i.e making some network requests). However, this doesn't work because the children component are not mounted, and so they don't start their loading process.
Is there a way to mount the children components so they can load?
Some approaches I've already considered:
Create a top level context that handles loading (i.e network requests) and let that control rendering. I can just render the children components once the context has finished its network requests.
Render the children components, but set display:none.
Are there any other approaches?

Your approaches sound reasonable. As soon as React 18 is released, however, Suspense will be officially available. It is built for exactly this use case, but currently only experimental support is available. Check out the react docs for more info.

Related

How can i trigger an update from one Table to another Table in React Typescript

in React Typescript i have a parent component with two independant child components:
export const Editor = () => {
return (
<>
<TableTotal />
<hr />
<TableDetail />
</>
);
};
And after some actions in TableDetail i have to completely reload data in TableTotal component.
In TableTotal i have code like this:
useEffect(() => {
getData();
}, []);
where getData is a function with fetch from the server.
How can i told the TableTotal component to reload data from the TableDetail Component (calling the getData() function) ?
I use React with Typescript.
Thanks
The concept you need to solve this is called lifting state up in the React documentation. You store the data shared by TableTotal and TableDetail inside Editor, and then pass the data to each child component as props. That way, if one component causes the data to change, the other one will re-render with the new data, too.
There are 2 solutions out there. Either use the prop drilling method or take your app states to global (context/redux).
Prop Drilling: Initialize state in the Editor Component, if you change something in any component, make that state dependency of the useEffect and it will call getData() and re-render the components.

Passing one Prop vs Many Props vs Nested Components in React?

This is a part of Think in React.
Thinking in React is the hard part for me because I see many developers do React with different mindsets.
When I was writing code for the Notification component that will be used by developers, suddenly I've noticed that there are different experiences to use the component:
Passing many Props like Bootstrap
<Notification
title="New Feature"
body={message}
action={action}/>
Passing one Prop as an Object
const data = {
title:"",
subtitle:"",
message:""
}
<Notification data={data}/>
Passing nested Children
<Notification>
<Title></Title>
<Body><Body/>
<Action><Action>
</Notification>
I followed the passing nested Children because ( I guess) It seems if I scale the component, I don't need to provide a Bootstrap-like experience for the developers.
import React from "react"
import { Wrapper, Text } from "./Styled"
const Body = ({ message }) => (
<Wrapper>
<Text>{message}</Text>
</Wrapper>
)
export default Body
The problem is I'm thinking about it is when I want to scale the Component and let's say adding 3 additional features that require 3 additional props
I'm confused about the reasons why each approach might be chosen, and what's the "best" developer experience.
To answer this question let's review all possibilities given React Element and a Function Component:
const c1 = <div>React Element</div>;
const C2 = () => <div>Function Component</div>;
Notice that from performance perspective, every component can be memoized and not cause useless renders.
Pass React element through props
const ObjectProps = ({ component }) => {
console.log("render object props");
return component;
};
<ObjectProps component={c1} />
Pros
Simple.
Lets you define the exact usage of passed component (contract).
For example you might decide "messages" have to be on top of "actions"
Cons
No lazy rendering
Passing heavy component may cause performance issues.
Hard to inject props (need to use React.cloneElement).
As a library writer you would like to inject your styles and refs.
Passing Function Component
const FunctionComponentProps = ({ FunctionComponent }) => {
console.log("render function component");
return <FunctionComponent />;
};
<FunctionComponentProps FunctionComponent={C2} />
Pros
Lazy rendering.
Easy to pass props and inject props for inner implementation.
Lets you define the exact usage of passed component (contract).
Cons
Confusing
Children Render
const ChildrenRender = ({ children }) => {
console.log("render function component");
return children;
};
<ChildrenRender>
{c1}
<C2 />
</ChildrenRender>
Pros
No restriction for the developer (no contract)
Cons
Hard to inject children (need to use React.Children API + React.cloneElement combo)
No contract
The developer might pass "buttons" and then "messages" and break the view.
Implementing ChildrenRender usually comes with component instances which results a minimal "contract" mentioned above.
const ChildrenRender = ({ children }) => {...};
ChildrenRender.InnerComp1 = <SomeComponent .../>
ChildrenRender.InnerComp2 = <SomeComponent2 .../>
<ChildrenRender>
<ChildrenRender.InnerComp1>{c1}</ChildrenRender.InnerComp1>
<ChildrenRender.InnerComp2><C2/></ChildrenRender.InnerComp2>
</ChildrenRender>
In Conclusion
It heavily depends on the component's usage, usually the hybrid approach suits well - passing components through props and add an option for passing children too.
Another technique is Render Props.

How do I pass things between components on the same level?

I've got a React app of the form:
<App>
<ParentComponent>
<FormComponent>
<AnotherComponent>
</ParentComponent>
</App>
I want to be able to update some state values of <FormComponent> by clicking on elements in <AnotherComponent>.
I don't want to place them inside each other, but keep them side-by-side. I don't want to lift up <FormComponent> state as it's nicely encapsulated.
What's the best way to achieve this? Can I do it with just react or do I need RxJS/something else?
The data in React Flows Down.
If you don't want to lift the state up, what's left is Context API or any state management library like Redux and MobX (both implement Context API with different strategy).
But still, the state is "above" FormComponent (you still lifting state up).
const Context = React.createContext();
const ParentComponent = () => {
const contextState = useState(DEFAULT_STATE);
return (
<Context.Provider value={contextState}>
<FormComponent />
<AnotherComponent />
</Context.Provider>
);
};
const FormComponent = () => {
const [, setState] = useContext(Context);
// use setState
};
const AnotherComponent = () => {
const [state] = useContext(Context);
// valid state updated from FormComponent
};
As far as I can tell the "right thing" to do in these instances is move the state up one level, into your Parent component.
If you have a look at the Intro to React:
To collect data from multiple children, or to have two child components communicate with each other, you need to declare the shared state in their parent component instead.
"Lifting state up" is a common thing in React applications and doesn't require introducing a state management solution like Redux or RxJS.
Apart from the ways mentioned above you can pass down a function as prop from the Parent component to AnotherComponent. And when clicking any element in Another component, pass the intended value in that function, which will in turn be transferred to ParentComponent. And you can then pass the value as props to the FormComponent from the ParentComponent.
You can check this example for better understanding https://react-m9skpu.stackblitz.io

sharing data between disconnected components

I'm not creating a react app from scratch, but adding interactive components to an existing webpage. I'm mounting two components, disconnected to each other like this:
ReactDOM.render(<Component1 />, document.getElementById('comp1-root'));
ReactDOM.render(<Component2 />, document.getElementById('comp2-root'));
They lie far from each other on the page.
How do I have them to communicate their states with each other?
Thank you.
React Portals is what I was looking for.
As per my question, I wanted to have same context for all the components mounted at different locations (dom nodes).
Portals solved exactly this issue. Now, I can have one context component housing all the components that exist on that page. Like this:
const dashboardContextDom = document.getElementById('dashboard-root');
const comp1DOM = document.getElementById('comp1-root');
const comp2DOM = document.getElementById('comp2-root');
const Dashboard = () => {
return (
<>
{ReactDOM.createPortal(<Component1 />, comp1DOM)}
{ReactDOM.createPortal(<Component2 />, comp2DOM)}
</>
);
}
if(dashboardContextDom) {
ReactDOM.render(<Dashboard />, dashboardContextDom);
}
With these components housed in one context allows to easily pass state from one component to another via prop drilling and lifting state up.
You have two options with React:
Move Component 1 and Component 2 and their state into a parent component, and pass the parent's update state function to them:
ComponentWrapper {
...
const updateState = () => {
//update state
}
return (
<Component 1 updateParentState={updateState}/>
<Component 2 updateParentState={updateState}/>
)
...
}
You cannot update the state of a component that is not a parent or a child without using an external state management solution, such as Redux or the new useContext api/hook. These libraries involve moving all individual component's states to a larger centralized global state, which can be updated and accessed by all components.

React Context creates troubles with Apollo applied through compose

I'm using React Context to communicate between components (actions)
Context.Consumer resides inside the return() part of a component and as such doesn't provide props to the component (unless Consumer is put in parent component but then we're back to the badness of props drilling)
Apollo client is applied by Apollo's compose() to that the result is available in component props but more importantly, a change in props leads to Apollo refetch
Using Context, this automatic refetch pathway is broken and I see no other way but to bring the whole GraphQl document into the component (after Context.Consume) to manually do a refetch when Context brings in action.
This is a radical change in pattern and I really wonder if I'm missing some big picture?
const Child = ({ gqlGetData }) => {
if (gqlGetData.loading) return "..."
const { componentData } = gqlGetData
return (
<AppControl.Consumer>
{appControl => (
<React.Fragment>
---component rendering logic here ---
---but needs also refetch logic and GraphQl document---
)}
</React.Fragment>
)}
</AppControl.Consumer>
)
}

Resources