Prevent rerender of sibling component which initiates a useState in wrapper component - reactjs

I am not very experienced with React but I have a very simple Setup.
export default function App() {
const [title, setTitle] = useState("still-empty");
const myFunction = title => {
setTitle(title);
};
return (
<div className="App">
<ComponentA myFunction={myFunction} />
<br />
<br />
<ComponentB title={title} />
</div>
);
}
const ComponentA = ({ myFunction }) => {
console.log("Rendering Component A");
return (
<div onClick={() => myFunction(Math.random() * 1000)}> Component A </div>
);
};
export default ComponentA;
const ComponentB = ({ title }) => {
return <div> Title : {title}</div>;
};
export default ComponentB;
Here is a sandbox to test this: https://codesandbox.io/s/musing-cookies-g7szr
See that if you click on "ComponentA", that exact ComponentA gets rerendered (you can see it in console) although no props are changed on this component. This is a simplified example of my real use case. In my real use case, ComponentA is a map where a lot of stuff (zoom, center)
will be reset. I want to prevent these resets and also the 1 second it takes for rerendering. Therefor I present this simplified example.
So how do I pass an information from ComponentA to ComponentB, without rerendering ComponentA itself? Thanks for helping out here.

use useCallback in Parent so that the function is not created again and again but only on initial render.
use React.memo so that when no props are changed the component wont re-render.
App
export default function App() {
const [title, setTitle] = useState("still-empty");
const myFunction = useCallback(title => {
setTitle(title);
}, []);
return (
<div className="App">
<ComponentA myFunction={myFunction} />
<br />
<br />
<ComponentB title={title} />
</div>
);
}
ComponentA
import React, { memo } from "react";
const ComponentA = ({ myFunction }) => {
console.log("Rendering Component A");
return (
<div onClick={() => myFunction(Math.random() * 1000)}> Component A </div>
);
};
export default memo(ComponentA);
Working demo is here:
https://codesandbox.io/s/affectionate-boyd-v7g2t?file=/src/App.js

Related

Is it ok to use react state in render prop?

I have two components App and MyComponent, where MyComponent is used in App.
import { useState } from "react";
import { MyComponent } from "./myComponent";
export const App = () => {
const [state, setState] = useState(0);
return (
<>
<MyComponent
render={() => (
<button onClick={() => setState((prev) => prev + 50)}>{state}</button>
)}
/>
</>
);
}
export const MyComponent = (props) => {
const Content = props.render;
return (
<div>
<Content/>
</div>
);
};
Is it ok to use state in the return value of the render prop? Is it considered anti-pattern?
Is it ok to use react state in render prop?
Yes, but... why? children prop was created to achieve exactly what you want here.
<MyComponent>
<button onClick={() => setState((prev) => prev + 50)}>{state}.</button>
</MyComponent>
export const MyComponent = ({ children }) => (
<div>
{children}
</div>
);

how can I call a method that is inside of Component? (I can't pass this method in props)

this seems like a duplicate question but it is not, the examples I have seen explain how to pass a function through props.
I have three components: ComponentA, ComponentB and App (App is the component root). I want to call a function contains in ComponentA using a button that is inside of ComponentB.
import "./styles.css";
import ComponentA from "./componentA";
import ComponentB from "./componentB";
export default function App() {
return (
<div className="App">
<>
<ComponentA>
<ComponentB />
</ComponentA>
</>
</div>
);
}
const ComponentA = ({ children }) => {
const hello = () => {
alert("hello");
//In my real scenario, this method contains a big logic...
};
return (
<>
Component A<div>{children}</div>
</>
);
};
const ComponentB = () => {
const callComponentAFunction = () => {
// I need call "hello()" from ComponentA
};
return (
<>
<button onClick={callComponentAFunction}>
Call function from ComponentA
</button>
</>
);
};
How can I call hello() (function inside of ComponentA) from ComponentB?
this is my live code
You can achieve that in many ways. Pass that function as props from component A.
Working example Modified code
//App.js
import "./styles.css";
import ComponentA from "./componentA";
// import ComponentB from "./componentB";
export default function App() {
return (
<div className="App">
<>
<ComponentA />
</>
</div>
);
}
//Component A
import ComponentB from "./componentB";
const ComponentA = ({ children }) => {
const hello = () => {
alert("hello");
};
return (
<>
Component A<div>{children}</div>
<ComponentB hello={hello} />
</>
);
};
export default ComponentA;
//Component B
const ComponentB = ({ hello }) => {
return (
<>
<button onClick={hello}>Call function from ComponentA</button>
</>
);
};
export default ComponentB;
you can also use the React.Children.map, the example like this: https://codesandbox.io/s/hardcore-ellis-ksgkd?file=/src/componentB.js

React Hooks: Independent state in dynamic children

I am generating my state in the parent component. latestFeed generates a series of posts from my backend:
import React, { useState, useEffect } from "react";
import { getLatestFeed } from "../services/axios";
import Childfrom "./Child";
const Parent= () => {
const [latestFeed, setLatestFeed] = useState("loading");
const [showComment, setShowComment] = useState(false);
useEffect(async () => {
const newLatestFeed = await getLatestFeed(page);
setLatestFeed(newLatestFeed);
}, []);
return (
<div className="dashboardWrapper">
<Child posts={latestFeed} showComment={showComment} handleComment={handleComment} />
</div>
);
};
export default Parent;
then latestFeed gets generated into a series of components that all need to hold their own state.
import React, { useState } from "react";
const RenderText = (post, showComment, handleComment) => {
return (
<div key={post._id} className="postWrapper">
<p>{post.title}</p>
<p>{post.body}</p>
<Comments id={post._id} showComment={showComment} handleComment={() => handleComment(post)} />
</div>
);
};
const Child= ({ posts, showComment, handleComment }) => {
return (
<div>
{posts.map((post) => {
return RenderText(post, showComment, handleComment);
})}
</div>
);
};
export default Child;
In its current form, the state of RenderText's is all set at the same time. I need each child of Child to hold its own state.
Thank you!
Instead of using RenderText as a function, call it as a component:
{posts.map((post) => (
<RenderText key={post.id} post={post} showComment={showComment} />
))}
This is because when used as a component, it will have it's own lifecycle and state. If used as a function call, React does not instantiate it the same way - no lifecycle, no state, no hooks, etc.

How to call a Parent method in Child Component using Hook (ForwardRef concept)

I tried the following code but it fails
So, this is my Parent Component:
import React from 'react'
import ChildComponent from './ChildComponent';
const ParentComponent = (props) => {
//step 1
// const inputRef = React.createRef();
const buttonRef = React.useRef();
const focusHandler = () => {
alert("hi");
}
return (
<div>
{/* In parent, we generally pass reference to child which we dint do here, lets see if props children help here */}
{props.children}
<ChildComponent ref="buttonRef" />
</div>
)
}
export default ParentComponent;
This is my child component:
import React from 'react'
const ChildComponent = React.forwardRef((props, ref) => {
return (
<div>
<button onClick={ref.focusHandler}>Focus Input</button>
</div>
)
})
export default ChildComponent;
On click of the button above in child component, I wish to call Parent method.
How can that be achieved?
EDITED
The reason you're getting the error is because refs in function components need to be passed using ref={buttonRef}, not ref="buttonRef". Class components have a thing they can do with string refs, but it's not recommended even there.
As for calling a function from a parent component, you don't need refs to do this. So if that was the only reason you were using a ref, you can remove the ref. Instead, pass the function as a prop:
const ParentComponent = (props) => {
const focusHandler = () => {
alert("hi");
}
return (
<div>
<ChildComponent focusHandler={focusHandler} />
</div>
)
}
const ChildComponent = (props) => {
return (
<div>
<button onClick={props.focusHandler}>Focus Input</button>
</div>
)
}
Just replace ref by focusHandler like below in parent component
<ChildComponent focusHandler={focusHandler} />
Then in ChildComponent, remove ref as well.
If you wonder how to use refs in this case (even though this is not the recommended way to pass callbacks), you need to assign focusHandler key and use the ref with ref.current, refer to Components and Props docs.
const ParentComponent = () => {
const buttonRef = React.useRef({ focusHandler: () => alert("hi") });
return (
<div>
<ChildComponent ref={buttonRef} />
</div>
);
};
const ChildComponent = React.forwardRef((props, ref) => {
return (
<div>
<button onClick={ref.current.focusHandler}>Focus Input</button>
</div>
);
});

Data Sharing between Sibling Functional Components - ReactJS

I have the main component as below.
const MainApp: React.FC = () => {
return (
<div>
<DummyComp/>
<ComponentA />
<ComponentB />
</div>
);
}
export default App;
"Component B" has some form elements and when it's value changes, I require all the form values to be passed to the "Component A".
All the examples that I found are not using Functional Components. So I am not sure how to create callback functions and use states in Functional Components.
const MainApp: React.FC = () => {
const [formData, setFormData ] = useState({});
return (
<div>
<DummyComp/>
<ComponentA formData={formData} />
<ComponentB onClick={setFormData} />
</div>
);
}
export default App;
You can edit formData object as you wish in ComponentB with setFormData method, and you can read the formData object in ComponentA.
You can either use Redux (Global State) to do this, OR use a state manage in the Main Component.
const MainApp: React.FC = () => {
const [data, setData] = useState();
useEffect(() => {
setData() //anyhting initial
},[formdata]);
const handleData = (data) => {
setData(data)
}
return (
<div>
<DummyComp/>
<ComponentA data={data} />
<ComponentB onformclick={handleData} />
</div>
);
}
export default App;
Lets Suppose component A is like
const ComponentA: React.FC = ({props}) => {
const data = props.data;
return (
<div>
//any html here
//example
<div>
{
data ?
data.property //any property existing on data
:null
}
</div>
</div>
);
}
export default ComponentB;
Lets Suppose component A is like
const ComponentA: React.FC = ({props}) => {
const [formdata, SetformData] = useState();
return (
<div>
//form here
//any button or any element to submit form
<button onClick={()=>props.onformclick(formdata)}> submit </button>
</div>
);
}
export default ComponentB;

Resources