Data Sharing between Sibling Functional Components - ReactJS - 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;

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

Reusing a variable in other components with state

I'm trying to reuse the variable msg in another component which is presumably scoped to the component it's declared in. The problem is the variable uses state and cannot be declared above the scope to be accessible to both components.
The App component that tries to use the component shows this error: msg is not defined
What's the best workaround to solve this issue?
import React, { useState } from "react";
const Test = () => {
const [msg, setMsg] = useState(null);
return (
<>
<p>state value from component: {msg}</p>
<button onClick={() => setMsg("changed")}>change state</button>
</>
);
};
const App = () => {
return (
<div>
{msg && ( // error here
<p>show this</p>
)}
<p>some text</p>
<Test />
</div>
);
};
export default App;
Here's a stackblitz for convenience: https://stackblitz.com/edit/react-g16stb
Thanks for any help here
you can't pass variables to parent or siblings you need to lift up state. find a common ancestor to place your state and pass down as props.
your code should look like:
const Test = ({ msg,setMsg }) => {
return (
<>
<p>state value from component: {msg}</p>
<button onClick={() => setMsg("changed")}>change state</button>
</>
);
};
const App = () => {
const [msg, setMsg] = useState(null);
return (
<div>
{msg && (
<p>show this</p>
)}
<p>some text</p>
<Test msg={msg} setMsg={setMsg} />
</div>
);
};
export default App;
In-order to access msg in the App component it is difficult to access in the parent component from the above code, So we need to lift up the state and place msg in the App component and pass it as a prop to the Test component
import React, { useState } from "react";
const Test = ({ msg,setMsg }) => {
return (
<>
<p>state value from component: {msg}</p>
<button onClick={() => setMsg("changed")}>change state</button>
</>
);
};
const App = () => {
const [msg, setMsg] = useState(null);
return (
<div>
{msg && (
<p>show this</p>
)}
<p>some text</p>
<Test msg={msg} setMsg={setMsg} />
</div>
);
};
export default App;

How to fetch data once that will be used by all Reactjs Components

I'm a beginner and read many example of how to fetch remote data in Components typically in the componentDidMountlife cycle method.
I have 7 Components that all use the same data a json file. I dont want to fetch this data in all of the Components. Is there some method of doing this before the app start so to speak? and store the data globaly
One option is to fetch the data just once in the topmost component, then pass it as props to each child (recursively, if needed). Using hooks:
const App = () => {
const [data, setData] = useState();
useEffect(() => {
api().then((result) => {
setData(result);
});
}, []);
return (
<div>
<Component1 data={data} />
<Component2 data={data} />
<Component3 data={data} />
</div>
);
};
You could also fetch the data before the initial ReactDOM.render, and pass the data as a prop to the top component.
You can also use useContext to avoid having to manually pass data everywhere:
const GlobalContext = createContext();
const App = () => {
const [data, setData] = useState();
useEffect(() => {
api().then((result) => {
setData(result);
});
}, []);
return (
<GlobalContext.Provider value={data}>
<div>
<Component1 />
<Component2 />
<Component3 />
</div>
</GlobalContext.Provider>
);
};
const Component1 = () => {
const data = useContext(GlobalContext);
// use data
};

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

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

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

Resources