Pass data to one of the same components - javascript

<Comp1 />
<div>
<Comp1 />
<Comp2 />
</div>
I am new to React. I want to pass data from Comp2 to its sibling Comp1 only. I know using a parent component to pass props but in this case I have to rewrite Comp1 to get state from its parent, which will affect all the Comp1. How can I make only chosen Comp1 receive the data and don't bother the else?

There is not a straightforward solution to this, but you do have a couple of options:
Option 1
The most direct way would be as you described - having Comp2 pass data up to its parent using an event listener, then having the parent pass it back down to Comp1. This can be an optional prop being passed to Comp1, so it doesn't matter that your outer Comp1 won't receive that prop.
For example:
import React from 'react';
const Comp1 = ({data='Default Value'}) => (
<p>{data}</p>
)
const Comp2 = ({onData}) => (
<button onClick={e => onData(Math.random())}>Change Value</button>
)
export default function App() {
let [data, setData] = React.useState(null);
return (
<div>
<Comp1/>
<div>
<Comp1 data={data}/>
<Comp2 onData={setData}/>
</div>
</div>
);
}
This is probably your best option, and by the sound of things, it might be good to find a way to refactor your app so that this option becomes more viable. There's usually a way to change your app structure to make this work better.
If you really want siblings to have a more direct line of communication with each other, you could give Comp1 a ref of Comp2, but I wouldn't encourage this.
Option 2
Another option would be to use contexts. This gives anyone the power to communicate with anyone who uses the same context. There is a lot of power in this feature. Some people set up a Redux-like system using contexts and reducers to let any part of the application (or larger component they put the context provider in) communicate with any other part. See this article for more information on using contexts to manage application state.
import React from 'react';
let context = React.createContext()
const Comp1 = () => {
let ctx = React.useContext(context) || {};
return <p>{ctx.data || 'Default Value'}</p>
}
const Comp2 = () => {
let ctx = React.useContext(context);
return <button onClick={e => ctx.setData(Math.random())}>Change Value</button>
}
export default function App() {
let [data, setData] = React.useState();
return (
<div>
<Comp1/>
<div>
<context.Provider value={{data, setData}}>
<Comp1/>
<Comp2/>
</context.Provider>
</div>
</div>
);
}
Option 3
For completeness, A third option would be using something like Redux to help share state. Only use this option if you are already using Redux, or if you really want/need it and understand what you're getting into. Redux is not for every project, everyone does not need it.
Side Note
I realize you said you were new to React. For brevity and for other Googlers, I used a lot of React hooks in my examples (The functions like React.useState, React.useContext, etc). These can take a little bit to understand, and I don't expect you to learn how to use them just to solve your problem. In fact, if you're new to React, I would strongly encourage you to just go with option 1 using the class syntax you've learned how to use already. As you get some more practice and start feeling the limits of the first option, then you can start trying the other things out.

In react, data always moves from top to down, so there is no true way to pass information sibling to sibling without going through some higher structure. You could use context, but again, its provider has to wrap around both sibling components, meaning it has to be implemented in the parent component(App). It is also intended for passing data between deeply nested sibling components to avoid passing props multiple levels deep. In your case where props only have to be passed one level deep, it is best to just store state in the parent component(App).
Here is what context would look like for your App (its more trouble than its worth at this point):
https://codesandbox.io/s/objective-hellman-sdm55?file=/src/App.js

For this use case I would suggest using the useState hook in the parent component and passing down a value & function to the specific child components.
pseudo code:
<Parent>
const [value, setValue] = useState();
<Comp1 onClick={setvalue} />
<Comp2 value={value} />
</Parent>
In my opinion, for your use case, Redux and the Context API are a bit overkill.

You can research about state and props.
References: https://flaviocopes.com/react-state-vs-props

Related

Best way to consolidate state dependent boiler plate in React Functional component

I'm trying to understand if there is a better design pattern to this use case.
I have a react functional component such as this.
const ComponentMajor = (props) => {
[state, setState]
classes = useStyles()
//image a whole bunch of other state.
return (
<div stateDependentProp1 stateDependentProp2>{stateDependent3}</div>
<div stateDependentProp1 stateDependentProp2>{stateDependent3}</div>
<div stateDependentProp1 stateDependentProp2>{stateDependent3}</div>)
//imagine each of these is maybe 50 lines of code and we need 10 of them.
//perfect use case for code re use.
}
So naturally I create a div component, but since it's so coupled with ComponentMajor and only needs to be used in ComponentMajor to eliminate code dupl, i want it to live inside the same file.
Option 1
const ComponentMajor = (props) => {
[state, setState]
classes = useStyles()
const boilerPlateComp = (props) => <div props>{stateDependent3}</div>
//image a whole bunch of other state.
return(
<boilerPlateComp props/>
<boilerPlateComp props/>
<boilerPlateComp props/>)
}
This isn't good because every time ComponentMajor re-renders it redeclares boilerPlateComp and has weird behaviors.
Option 2
declare boilerPlateComp OUTSIDE ComponentMajor - but this makes it inefficient to share scope (which if defined inside ComponentMajor is not an issue) between them. It results in some annoying boiler plate ironically to pass the scope dependencies in as props.
The cleanest I was able to come up with was something like this, but there HAS to be a better way to do this in FUNCTIONAL components.
const boilerPlateComp = ({sharedScope, props}) => {
let {stateDep1, stateDep2, stateDep3, etc} = sharedScope
return (<div props>{stateDependent3}</div>)
}
const ComponentMajor = (props) => {
[state, setState]
classes = useStyles()
let sharedScope = {state, setState, classes, etc}
//image a whole bunch of other state.
return(
<boilerPlateComp props sharedScope={sharedScope}/>
<boilerPlateComp props sharedScope={sharedScope}/>
<boilerPlateComp props sharedScope={sharedScope}/>)
}
My Question Is:
What is the standard design pattern for this common use case? Is this a scenario where a class component would make more sense?
I tried playing around with memoizing the components declared WITHIN ComponentMajor but didn't seem to work.
Is there a clean way to use javascript apply to pass the scope onto the component instances?
Your second option is the correct one. Fundamentally in React, when you need some state to live above a component, that state has to be "passed-down" to the child component (this is sometimes called "prop drilling", especially if it has to be passed through other components). In other words, you do need that sharedScope prop:
<boilerPlateComp props sharedScope={sharedScope}/>
However, you could use a map to not have to repeat lines like that:
{someArray.map(() => <boilerPlateComp props sharedScope={sharedScope}/>}
And if you really didn't want to pass props you could use Context instead ... although using it to avoid a single component's worth of "prop drilling" would be more of an anti-pattern.

Using state in other component

Hi I am learning and new to react and I want to know how to pass state from one component to other,
I have one component as
const [paneCount, setPaneCount]= useState(1);
const openPane = (paneKey) => {
setOpeningPaneKeys(oldState => {
if (!oldState.includes(paneKey)) {
return [...oldState, paneKey]
}
return oldState
})
setPaneCount(paneCount+1);
console.log(paneCount);
setFocusingPaneKey(paneKey)
}
where I want to use paneCount in App.js file
function App(props) {
const [inactive, setInactive] = useState(false);
return (
<div className="App">
<Header />
<Navbar
onCollapse={(inactive) => {
setInactive(!inactive);
}}
/>
<div class="landing-card">
<div>
<h4 class="headingStyle">Recorder Box</h4>
<h4>Count:{props.paneCount}</h4>
<img src="landing.jpg" alt="Forest" width="775" height="500"></img>
</div>
</div>
How to pass paneCount to App.js
You can't pass data upwards, only downwards. The reason for that is how the application and data flow is built. Luckily there are 3 ways to get it done.
1) Initialize in App.js
If you want to use state value in App.js but want to work with it somewhere else:
export default function App() {
const [myState, setMyState] = useState();
return <Component state={ myState } setState={ setMyState } />
}
This way you can keep all your state in 1 place and use it everywhere, but it also means that you have to pass component by component to do so.
2) Context
This improves the previous option, because you no longer need to pass data around. Instead you can keep it in a provider and use it throughout your application.
There are multiple ways to define a provider, so I will just link you the docs for that one.
Note: Provider definitions does not differ in any way. They are not practical or impractical, simply one's preference over another!
3) Redux
This is arguably the BEST option for state management (eventhough I don't like it..). I haven't used it yet and don't want to either, because the previous option does the same with less effort IMO.
Here's the docs for that one.
Instead of passing paneCount to App.js which I suppose is the parent component. You can create paneCount in App.js and then pass setPaneCount to the openPane component like this.
<openPane setPaneCount = {setPanecount} paneCount = {setPaneCount}/>
If you are not calling openPane in the App.js for some reason then go to the parent component that is calling both App and openPane and create and pass the setPaneCount and paneCount from there. If you making something complex instead of drilling the value down like this you might want to look at some stateManagement tools like Redux or Context API.

React - use the exact same component instance many times in the DOM

I have a React component that's a div, some styling, and text. That exact component with the exact same value is used many, many times in the DOM. I am trying to figure out how to create just one instance of that component, and simply present the exact same instance everywhere that I need it. I tried use memoization, but that seems to be memoizing the instance itself.
Just to be explicitly clear, I'd like to memoize the component such that calling the function with the same arguments from anywhere will return the exact same instance every single time.
const Component = React.memo((value) => (<div>...</div>));
Calling this seems to memoize the particular instance, but it will still create a brand new instance every single time it's invoked from a different place in the Virtual DOM, even if the arguments are identical. As I understand it, React.Memo is just a wrapper around a particular instance.
I've thought of some hacky ways to do this, but wanted to ask before exploring that further.
You can use Context https://reactjs.org/docs/context.html
const AppContext = React.createContext(null);
const App = () => (
<AppContext.Provider value={...//your component} >
//...
</AppContext.Provider>)
)
const ChildComponent = () => {
const Component = useContext(AppContext);
return (
<div>
//...
{Component}
</div>
)
}

Pass data to sibling component

I want to know how to pass props into sibling components like in the given image. I cant explain it in words because it seems too hard to understand(at least for me).
I have an App(Class component), with the given state variables inside of it. I use the state to generate an object which contains information like a calendar(dates, weeks, years). The buttons appearance are generated by this script.
The Time table component takes some props(based on which button is pressed), and those are used to collect data from Firestore Database.
I know React has a one-way data flow, from the top to the bottom, this is why I can't figure this out. I don't want to use Redux, if possible.
Q1: How can I tell the Time table component which button was pressed and what date it contains?
Q2: Do I really need to use Redux in these situations?
*Edit
Here is the draw.io chart I posted above if it helps anyone.
If you want to exchange props between sibling components wrapped within common parent, you may lift source component state up to the common parent's state then pass it down to another (sink) component.
If you need your source component's state to be globally accessible (e.g. to multiple non-adjacent components scattered across your app), you may want to use React Context or some state management tool, like Redux.
Live-demo of the first approach (at its very simplest) you may find as follows:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const Source = ({onButtonClick}) => (
<button onClick={onButtonClick}>Hit me</button>
)
const Sink = ({status}) => (
<div>{status}</div>
)
const Parent = () => {
const [buttonStatus, setButtonStatus] = useState('off'),
handleButtonHit = () =>
setButtonStatus(buttonStatus == 'off' ? 'on' : 'off')
return (
<div>
<Source onButtonClick={handleButtonHit} />
<Sink status={buttonStatus} />
</div>
)
}
render (
<Parent />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

Render Props vs HOC?

I'm trying to learn React from scratch and having a deep knowledge of concepts !
Today I was searching about HOC, Render Props and the differences between the two. I've checked render times for both. I wrote a console.log('rendered') into render to check render times in browser console.
HOC: When I used HOC to write an usable component, I saw after each changes on props I've render for HOC and component that used HOC.
Render Prop: In this case I've changed the props, but only wrapper component has rendered. because with render props we load only one component and inject codes to use that component feature !
So, Is it a benefit to use Render Props instead HOC components? Or HOC components are usable and powerful yet?
Thanks
HOC, Render Props and now hooks all serve to the same purpose: Share stateful logic between components. There is actually no way to tell which one is better or worst. All depends on your use case.
High Order Components are composable. It's easy to nest them
const withProps = (Component) => connect(mapState, mapDispatch)(<Component foo='bar' />)
Children as a function is a bad pattern for composability, nesting looks a lot like a callback hell cause they need to be executed inside an jsx block
const Component = () =>{
return(
<Consumer>
{
props =>(
<ThemeConsumer>
{
theme => <Child {...props} {...theme} />
}
</ThemeConsumer>
)
}
</Consumer>
)
}
On the other hand, render props it's easy to set up, have less boilerplate and in most cases are easier to reason about.
Hooks bring the best of both worlds
hooks are composable, can be easily nested, and are simple to reason about cause after all they're just plain old functions
const useConfig = () =>{
const customProps = useCustomProps()
const theme = useContext(ThemeContext)
return [customProps, theme]
}
const Component = () =>{
const [props, theme] = useConfig()
}
But again: There is no such thing as the best pattern. It's just a matter of where are you going to use it.

Categories

Resources