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.
Related
I have 2 react components that need to share a state, react-router shows component A, which takes some inputs and adds it to its state, after the state has been successfully updated, I want to redirect to component B, where the user adds some more inputs and updates the same state as component A to build an object with inputs from A and B before I submit a post request to my api to save the data from both component A and B. How can I accomplish this, is there a way to use react-router, or do I have to set up a parent/child relationship between the components?
The dependency type between the components will define the best approach.
For instance, redux is a great option if you plan to have a central store. However other approaches are possible:
Parent to Child
Props
Instance Methods
Child to Parent
Callback Functions
Event Bubbling
Sibling to Sibling
Parent Component
Any to Any
Observer Pattern
Global Variables
Context
Please find more detailed information about each of the approaches here
What you want is to implement some object that stores your state, that can be modified using callback functions. You can then pass these functions to your React components.
For instance, you could create a store:
function Store(initialState = {}) {
this.state = initialState;
}
Store.prototype.mergeState = function(partialState) {
Object.assign(this.state, partialState);
};
var myStore = new Store();
ReactDOM.render(
<FirstComponent mergeState={myStore.mergeState.bind(myStore)} />,
firstElement
);
ReactDOM.render(
<SecondComponent mergeState={myStore.mergeState.bind(myStore)} />,
secondElement
);
Now, both the FirstComponent and SecondComponent instances can call this.props.mergeState({ . . .}) to assign state to the same store.
I leave Store.prototype.getState as an exercise for the reader.
Note that you can always pass the store (myStore) itself to the components; it just feels less react-y to do so.
Here is some more documentation that might be of interest:
React Docs: "Communicate Between Components"
For communication between two components that don't have a
parent-child relationship, you can set up your own global event
system. Subscribe to events in componentDidMount(), unsubscribe in
componentWillUnmount(), and call setState() when you receive an event.
Flux pattern is one of the possible ways to arrange this.
The easiest way to use a shared state between several components without rewriting your application's code to some state management system is use-between hook.
Try this example in codesandbox
import React, { useState } from "react";
import { useBetween } from "use-between";
// Make a custom hook with your future shared state
const useFormState = () => {
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
return {
username, setUsername, email, setEmail
};
};
// Make a custom hook for sharing your form state between any components
const useSharedFormState = () => useBetween(useFormState);
const ComponentA = () => {
// Use the shared hook!
const { username, setUsername } = useSharedFormState();
return (
<p>
Username: <input value={username} onChange={(ev) => setUsername(ev.target.value)} />
</p>
);
};
const ComponentB = () => {
// Use the shared hook!
const { email, setEmail } = useSharedFormState();
return (
<p>
Email: <input value={email} onChange={(ev) => setEmail(ev.target.value)} />
</p>
);
};
const ComponentC = () => {
// Use shared hook!
const { email, username } = useSharedFormState();
return (
<p>
Username: {username} <br />
Email: {email}
</p>
);
};
export const App = () => (
<>
<ComponentA />
<ComponentB />
<ComponentC />
</>
);
For first, we create useFormState custom hook as a source for our state.
In the next step, we create useSharedFormState hook who uses useBetween hook inside. That hook can be used in any component who can read or update the shared state!
And the last step is using useSharedFormState in our components.
useBetween is a way to call any hook. But so that the state will not be stored in the React component. For the same hook, the result of the call will be the same. So we can call one hook in different components and work together on one state. When updating the shared state, each component using it will be updated too.
I'll be going straight to hell for this:
// src/hooks/useMessagePipe.ts
import { useReducer } from 'react'
let message = undefined
export default function useMessagePipe(): { message: string | undefined, sendMessage: (filter: string) => void } {
const triggerRender = useReducer((bool) => !bool, true)[1]
function update(term: string) {
message = message.length > 0 ? message : undefined
triggerRender()
}
return { message: message, sendMessage: update }
}
You can then use this in any component anywhere in your applications' component hierarchy to send a message:
// src/components/ExampleInputToHell.jsx:
import useMessagePipe from 'src/hooks/useMessagePipe'
export const ExampleInputToHell() = () => {
const { sendMessage } = useMessagePipe()
return <input onChange={(e) => sendMessage('🔥 Hell-O 😈: ' + e.target.value)} />
}
… and consume the message any component anywhere in your applications' component hierarchy:
// src/components/ExampleOutputInHell.jsx
import useMessagePipe from 'src/hooks/useMessagePipe'
export const ExampleOutputInHell() {
const { message } = useMessagePipe()
return <p>{message}</p>
}
Explanation
let message outside the useMessagePipe-closure holds a global state, that (as far is the theory goes) gets surrounded in it's own module scope
as react's functional component logic will know nothing about that state, triggerRender – a version of a dirty hack that's actually mentioned on the React FAQ – needs to be applied to signal to react that all components consuming this function are asked to re-evaluate (re-render).
Disclaimer
This is a global state, meaning: all components using useMessagePipe see the same message and access the same update function, application-wide. If you want to have a new "channel" between two other components, you need to create another hook referring to another global state holder outside the closure (like message in this example).
If you know any better and have the time and resources, you probably don't want to go down this muddy road to perdition and instead learn how to properly useContext or (an easier way) give useBetween by #Slava Birch a star.
But if you just want a quick and dirty solution to pipe a piece of data between components right now … well this ~10 lines of code made my day for a simple task at hand and worked flawless so far. However my gut feeling says something is going to break if used for important things, hence any additions & theories on the conditions under which it will break are highly welcome.
Either you can set up a parent child relationship then you can pass data to child components as props.
Else, if you want to create interaction between 2 components which are not related to either(parent/child) you can either check out flux or even better redux.
I would say you should go with redux.See Here why
You can build custom React hooks to share a state between components, I made one here. You can use it by downloading use-linked-state.js file.
After importing useStateGateway hook, declare a gateway in parent component and pass it down to your child components
import {useStateGateway} from "use-linked-state";
const myGateway = useStateGateway({partA:null, partB:null});
return (
<>
<ComponentA gateway={myGateway}>
<ComponentB gateway={myGateway}>
<ComponentPost gateWay={myGateway}>
</>
)
Then you have access shared state between those three components by a custom useLinkedState hook
import { useLinkedState } from "use-linked-state";
export default function ComponentA({gateway}){
const [state, setState] = useLinkedState(gateway);
<your logic>
}
In your logic ComponentA and ComponentB would be responsible for their part in shared object {partA:"filled by ComponentA", partB:"filled by componentB"}.
Finally ComponentPost post the result if partA and partB of shared object were valid.
In this way you can compose components and make connection between them to talk to each other.
<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
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.
I have a component that is connected to a store using react-redux (I have shortened my code for simplicity sake):
const Address = (props) => {
const { userAddresses, removeAddress } = props
let { showEdit } = props
return (
<div>
{userAddresses.map(address => (
<div key={address.id}>
<p>{address.name}</p>
<Button onClick={removeAddress.bind(this, address)}>Delete</Button>
</div>
))}
<Button
onClick={() => { showEdit = true }}>
Add new
</Button>
{showEdit ? (
// show some edit stuff here!!!
): null}
</div>
)
}
const mapState = state => {
return {
userAddresses: state.account.userAddresses,
showEdit: false
}
}
const mapDispatch = (dispatch) => {
return {
addAddress: address => dispatch(addUserAddress(address)),
removeAddress: address => dispatch(removeUserAddress(address)),
}
}
export default connect(mapState, mapDispatch)(Address)
When you click the button (Add new), a form is supposed to popup (marked as show some edit stuff here!!!). I know this can be easily done if Address was a state component. However, I need to use react-redux, and as far as I know, you have to use a stateless component to use react-redux. Can anyone point me in the right direction?
No, you do not "have to use a stateless/function component" to use React-Redux!
connect accepts both class components and function components (and even "special" React components like React.memo()), and it doesn't matter whether they use component state (or hooks) inside or not.
Also, as a side note, you can simplify your code using the "object shorthand" form of mapDispatch:
const mapDispatch = {
addAddress : addUserAddress,
removeAddress : removeUserAddress
}
(Note that that could be even shorter if your prop names were named the same as the functions.)
Keeping to strictly use redux, you should have another action to dispatch when the user clicks the button. Then, a reducer will update the value of the showEdit property, which will cause a re-render of your stateless component allowing you to conditionally render the editing form.
But, this is an information (the visibility or not of the editing form) not useful to the rest of your application, so it could be the case to transform your component into a stateful one and track the showEdit property in the local state.
A third option could be the use of useState hook, but it depends on the version of React you have in your project, because they are currently in alpha...
I have 2 react components that need to share a state, react-router shows component A, which takes some inputs and adds it to its state, after the state has been successfully updated, I want to redirect to component B, where the user adds some more inputs and updates the same state as component A to build an object with inputs from A and B before I submit a post request to my api to save the data from both component A and B. How can I accomplish this, is there a way to use react-router, or do I have to set up a parent/child relationship between the components?
The dependency type between the components will define the best approach.
For instance, redux is a great option if you plan to have a central store. However other approaches are possible:
Parent to Child
Props
Instance Methods
Child to Parent
Callback Functions
Event Bubbling
Sibling to Sibling
Parent Component
Any to Any
Observer Pattern
Global Variables
Context
Please find more detailed information about each of the approaches here
What you want is to implement some object that stores your state, that can be modified using callback functions. You can then pass these functions to your React components.
For instance, you could create a store:
function Store(initialState = {}) {
this.state = initialState;
}
Store.prototype.mergeState = function(partialState) {
Object.assign(this.state, partialState);
};
var myStore = new Store();
ReactDOM.render(
<FirstComponent mergeState={myStore.mergeState.bind(myStore)} />,
firstElement
);
ReactDOM.render(
<SecondComponent mergeState={myStore.mergeState.bind(myStore)} />,
secondElement
);
Now, both the FirstComponent and SecondComponent instances can call this.props.mergeState({ . . .}) to assign state to the same store.
I leave Store.prototype.getState as an exercise for the reader.
Note that you can always pass the store (myStore) itself to the components; it just feels less react-y to do so.
Here is some more documentation that might be of interest:
React Docs: "Communicate Between Components"
For communication between two components that don't have a
parent-child relationship, you can set up your own global event
system. Subscribe to events in componentDidMount(), unsubscribe in
componentWillUnmount(), and call setState() when you receive an event.
Flux pattern is one of the possible ways to arrange this.
The easiest way to use a shared state between several components without rewriting your application's code to some state management system is use-between hook.
Try this example in codesandbox
import React, { useState } from "react";
import { useBetween } from "use-between";
// Make a custom hook with your future shared state
const useFormState = () => {
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
return {
username, setUsername, email, setEmail
};
};
// Make a custom hook for sharing your form state between any components
const useSharedFormState = () => useBetween(useFormState);
const ComponentA = () => {
// Use the shared hook!
const { username, setUsername } = useSharedFormState();
return (
<p>
Username: <input value={username} onChange={(ev) => setUsername(ev.target.value)} />
</p>
);
};
const ComponentB = () => {
// Use the shared hook!
const { email, setEmail } = useSharedFormState();
return (
<p>
Email: <input value={email} onChange={(ev) => setEmail(ev.target.value)} />
</p>
);
};
const ComponentC = () => {
// Use shared hook!
const { email, username } = useSharedFormState();
return (
<p>
Username: {username} <br />
Email: {email}
</p>
);
};
export const App = () => (
<>
<ComponentA />
<ComponentB />
<ComponentC />
</>
);
For first, we create useFormState custom hook as a source for our state.
In the next step, we create useSharedFormState hook who uses useBetween hook inside. That hook can be used in any component who can read or update the shared state!
And the last step is using useSharedFormState in our components.
useBetween is a way to call any hook. But so that the state will not be stored in the React component. For the same hook, the result of the call will be the same. So we can call one hook in different components and work together on one state. When updating the shared state, each component using it will be updated too.
I'll be going straight to hell for this:
// src/hooks/useMessagePipe.ts
import { useReducer } from 'react'
let message = undefined
export default function useMessagePipe(): { message: string | undefined, sendMessage: (filter: string) => void } {
const triggerRender = useReducer((bool) => !bool, true)[1]
function update(term: string) {
message = message.length > 0 ? message : undefined
triggerRender()
}
return { message: message, sendMessage: update }
}
You can then use this in any component anywhere in your applications' component hierarchy to send a message:
// src/components/ExampleInputToHell.jsx:
import useMessagePipe from 'src/hooks/useMessagePipe'
export const ExampleInputToHell() = () => {
const { sendMessage } = useMessagePipe()
return <input onChange={(e) => sendMessage('🔥 Hell-O 😈: ' + e.target.value)} />
}
… and consume the message any component anywhere in your applications' component hierarchy:
// src/components/ExampleOutputInHell.jsx
import useMessagePipe from 'src/hooks/useMessagePipe'
export const ExampleOutputInHell() {
const { message } = useMessagePipe()
return <p>{message}</p>
}
Explanation
let message outside the useMessagePipe-closure holds a global state, that (as far is the theory goes) gets surrounded in it's own module scope
as react's functional component logic will know nothing about that state, triggerRender – a version of a dirty hack that's actually mentioned on the React FAQ – needs to be applied to signal to react that all components consuming this function are asked to re-evaluate (re-render).
Disclaimer
This is a global state, meaning: all components using useMessagePipe see the same message and access the same update function, application-wide. If you want to have a new "channel" between two other components, you need to create another hook referring to another global state holder outside the closure (like message in this example).
If you know any better and have the time and resources, you probably don't want to go down this muddy road to perdition and instead learn how to properly useContext or (an easier way) give useBetween by #Slava Birch a star.
But if you just want a quick and dirty solution to pipe a piece of data between components right now … well this ~10 lines of code made my day for a simple task at hand and worked flawless so far. However my gut feeling says something is going to break if used for important things, hence any additions & theories on the conditions under which it will break are highly welcome.
Either you can set up a parent child relationship then you can pass data to child components as props.
Else, if you want to create interaction between 2 components which are not related to either(parent/child) you can either check out flux or even better redux.
I would say you should go with redux.See Here why
You can build custom React hooks to share a state between components, I made one here. You can use it by downloading use-linked-state.js file.
After importing useStateGateway hook, declare a gateway in parent component and pass it down to your child components
import {useStateGateway} from "use-linked-state";
const myGateway = useStateGateway({partA:null, partB:null});
return (
<>
<ComponentA gateway={myGateway}>
<ComponentB gateway={myGateway}>
<ComponentPost gateWay={myGateway}>
</>
)
Then you have access shared state between those three components by a custom useLinkedState hook
import { useLinkedState } from "use-linked-state";
export default function ComponentA({gateway}){
const [state, setState] = useLinkedState(gateway);
<your logic>
}
In your logic ComponentA and ComponentB would be responsible for their part in shared object {partA:"filled by ComponentA", partB:"filled by componentB"}.
Finally ComponentPost post the result if partA and partB of shared object were valid.
In this way you can compose components and make connection between them to talk to each other.