How to get a component to pass state to parent's sibling - reactjs

I have components as shown below
Component1 --- Component3
|
Component2
|
Component4
Components 2 and 3 are children of 1, and Component 4 is the child of 2.
I need to pass data from Component4 which I am storing as its state to Component3 which will display it. I presume the way to do this is to pass the state to from Component4 up to Component2 which will further send the state to Component1 using callbacks and finally Component1 will pass the state down to Component3. Is this the best way to do this?

Store the data in the state highest common parent (Component 1). Create methods on component1 that manipulate this state and bind this to them in the constructor. Then pass the state down to component3.
class component1 extends React.Component {
constuctor(props) {
super(props);
this.stateChanger = this.stateChanger.bind(this)
this.state = {
foo: 'bar'
}
}
stateChanger(e) {
this.setState({ foo: 'baz' })
}
render() {
<Component3 foo={this.state.foo} />
<Component2 stateChanger={this.stateChanger} />
}
}
edit: pass the stateChanger function down to component4 like below:
class Component2 extends React.Component {
render() {
return (
<div>
<Component4 stateChanger={this.props.stateChanger} />
</div>
)
}
}
Then do what you will with it.
Here is an article you should check out!

Callbacks will work. Another common solution is to lift the state up.
Instead of storing the state in Component4, simply move the state to Component1 and pass them down as props to Component3 and 4.
Personally I don't like a long callback chain, lifting the state is usually feels better to me.
If you have a lot of state that need to be shared between components, you should start to consider a state management solution, like Redux or MobX etc.

Best and scalable solution for this problem is using Flux or Redux which helps in the bidirectional data flow. They have a store where the data is stored, and whenever we want we can access and modify the data which is available to all components.
Have a look onto Redux and you can find examples implemented using Redux here

Related

const value between class components in React

I have a class component which renders a const. all the values needed for its evaluation are inside that component. However for purposes of splitting up and keeping the code clean, it is used in another class component. How do i connect them?
Code:
Class component1 extends React.Component
///code
render() {
const {allFacts, currentPage, factsPerPage } = this.state
const indexOfLastFact = currentPage * factsPerPage
const indexOfFirstFact = indexOfLastFact - this.state.factsPerPage
const allFactsSliced = allFacts.slice(indexOfFirstFact, indexOfLastFact) <-- THIS
}
///
export default component1
the last const, allFactsSliced, is then used in another component:
Class component2 extends React.Component
///code
render() {
const renderAllFacts =
this.state.isLoading ? <div id="loading">///</div> :
allFactsSliced.map((fact, index) => <--- HERE
{return <div>Fact # {index +1}: <br/> {fact.fact}</div>})
is this possible, good practice, or am i just needlesly complicating things and should keep everything in one component?
You can pass information into React components either by props, context, or a pure JS function or object.
So, if you want to pass allFactsSliced (or any other data) from one component to another, you have to do that by either props or context (since that particular piece of data is derived from the state of a component, you can't do it by a pure JS function or object).
If you have a parent/child relationship between these components, then you can pass derived state from the parent to the child (here it would have to be component1 to component2). If they are siblings, then you have to lift the state (see the React docs: https://reactjs.org/docs/lifting-state-up.html).
If it's more a grandparent or great-grandparent, then you can consider putting that derived state from component1 into a context and having component2 consume that context.
It's worth mentioning that you'll re-render any components that rely on that prop or context when it changes it, so it may be faster to keep it in one component. Or it might not be. Profile it and check if it matters.
But, if you want to pass a value from one component to another, it's always either as a prop or a context.

Sharing state among subcomponents in React

I'm currently developing an app that uses React in some parts of its UI to implement some complex feature that are very interactive. Some of these parts of the app, that are in fact React components, have become very complex in size.
This has led me to organize the code by dividing them in multiple subcomponents (and those in their own subcomponents, and so forth). The main problem is that some of the grandchildren of the main component might have the need to modify the state of another grandchildren that they're not related to. To do so, I end up having to have the state in the common parent and passing a lot of event handlers (methods of the parent component) to their children, and those children would need to pass it to their grandchildren.
As you can imagine, this is becoming some kind of a mess.
// MyComponent.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [1, 2, 3, 4],
selected: '',
}
this.add = this.add.bind(this)
this.handleChange = this.handleChange.bind(this)
}
add() {
const newNumber = this.state.list[this.state.list.length - 1] + 1,
list = [...this.state.list, newNumber]
this.setState({list})
}
handleChange({target}) {
this.setState({
selected: target.value,
})
}
render() {
return (
<>
<List items={this.state.list} selected={this.state.selected} />
<Button onClick={this.add} />
<input type="text" value={this.state.selected} onChange={this.handleChange} />
</>
)
}
}
// Button.js
class Button extends React.Component {
render() {
return (
<button onClick={this.props.onClick}>Click me!</button>
);
}
}
// List.js
class List extends React.Component {
constructor(props) {
super(props)
this.refs = props.items.map(_ => React.createRef())
}
render() {
return (
<ul>
{this.props.items.map((item, key) =>
(<li ref={this.ref[key]} key={key}>{item}</li>)
)}
</ul>
);
}
}
In the previous dummy code you can see how I need to define the add() method in the MyCompoent component so that an action that happens in the Button component can modify what is being shown in List. Even tho this might seem like the obvious way to do it, my component has a big component tree, and a lot of methods, and most of then are lost in the tree, passing from parent to child until it reaches the component that should be expected.
I have done some research on the internet and it turns out this is a very common problem. In most sites, using Redux or other state management library is recommended. However, all the tutorials and guides I've seen that implement Redux with React seem to assume you're only using React to build your app, in Single Page Application sort of way. This is not my case.
Is there any way to share the state of a component to avoid this kind of problem? Is there, maybe, a way to use Redux multiple times for multiple components in the same app, where one store saves only the state for MyComponent and can be accessed by either List or any of its possible children?
Redux doesn't require your entire site to be in React. It implements a higher-level component that you can use with any React components even if they are embedded in another site.
You can look at React hooks to solve similar problems. Specifically, check out useContext() and useState().
You've used a lifting state up pattern in react in your example.
It's quite common you good approach but when you app is growing you need to pass all bunch of props throu the tree of components. It's difficult to maintain.
In this case you need to check out redux with separated store or useContext() hook.

How do I pass a class's state as props in another class?

I want to create a React App that fetches data based on filters given by the user. I have two classes, Filters and News. In Filters, I want to set sourceName and countryCode as state; then fetch based on these parameters in News. How do I pass the state values as props in News?
The best way is to pass the state from the parent of both elements.
import React, { Component } from "react";
export default class Parent extends Component {
constructor() {
super();
this.state = {
sourceName: null,
contryCode: null
}
}
render() {
return (
<div>
<Filters {...this.state} />
<News {...this.state} />
</div>
)
}
}
You should always keep state in an element higher up in the render chain then all the children that need it. Doing this allows you to trickle down the state to all children that need it. Then only time state should be maintained within a component is if that component is the only component that needs it.
you can simple pass the state down as props in the child.
like <News filter={this.state.filer} />.

Where to keep active user data on React + Redux client application

On my React + Redux client app, I need to get the active user info (fetch from myapp.com/users/me) and keep it somewhere so that I can access it from multiple components.
I guess window.activeUser = data would not be the best practice. But I could not find any resource about the best practice of doing that. What would be the best way to do what I want?
you can keep it in a separate reducer, and then import multiple parts of your state with connect() in your components.
Say if you have 2 reducers called users.js and tags.js which are combined with combineReducers when setting up your store. You would simply pull different parts by passing a function to your connect() call. So using es6 + decorators:
const mapStateToProps = state => {
return {users: state.users, tags: state.tags}
}
#connect(mapStateToProps)
export default class MyComponent extends React.Component {
and then down in your render function:
return (
<div>
<p>{this.props.users.activeUsernameOrWhatever}</p>
<p>{this.props.tags.activeTags.join('|')}</p>
</div>
);
So your different reducer states become objects on this.props.
You can use React's context, HOC or global variables to make your data available to multiple components, via context
Something like...
class ParentDataComponent extends React.Component {
// make data accessible for children
getChildContext() {
return {
data: "your-fetchet-data"
};
}
render() {
return < Child />
}
}
class Child extends React.Component {
render() {
// access data from Parent's context
return (<div> {this.context.data} </div>);
}
}
create an action creator and call it on componentWillMount of the appropriate component so it runs right before your component mounts, and in the action creator fetch the data you need and pass it to a reducer. in that reducer you can keep the data you want throughout your application. so whenever you needed the data you can retrieve it from redux state. this tutorial from official redux website covers everything you need to know. mention me if you had any questions.

Updating state in more than one component at a time

I have a listview component which consists of a number of child listitem components.
Each child listitem have a showSubMenu boolean state, which display a few extra buttons next to the list item.
This state should update in response to a user event, say, a click on the component DOM node.
childcomponent:
_handleClick() {
... mutate state
this.props.onClick() // call the onClick handler provided by the parent to update the state in parent
}
However, it feels somewhat wrong to update state like, as it mutates state in different places.
The other way i figured i could accomplish it was to call the this.props.onClick directly, and move the child state into the parent as a prop instead, and then do change the state there, and trickle it down as props.
Which, if any, of these approaches is idiomatic or preferable?
First of all, I think that the question's title doesn't describe very well what's your doubt. Is more an issue about where the state should go.
The theory of React says that you should put your state in the higher component that you can find for being the single source of truth for a set of components.
For each piece of state in your application:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy).
Either the common
owner or another component higher up in the hierarchy should own the
state.
If you can't find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
However, a Software Engineer at Facebook said:
We started with large top level components which pull all the data
needed for their children, and pass it down through props. This leads
to a lot of cruft and irrelevant code in the intermediate components.
What we settled on, for the most part, is components declaring and
fetching the data they need themselves...
Sure, is talking about data fetched from stores but what im traying to say is that in some cases the theory is not the best option.
In this case i would say that the showSubMenu state only have sense for the list item to show a couple of buttons so its a good option put that state in the child component. I say is a good option because is a simple solution for a simple problem, the other option that you propose means having something like this:
var GroceryList = React.createClass({
handleClick: function(i) {
console.log('You clicked: ' + this.props.items[i]);
},
render: function() {
return (
<div>
{this.props.items.map(function(item, i) {
return (
<div onClick={this.handleClick.bind(this, i)} key={i}>{item} </div>
);
}, this)}
</div>
);
}
});
If, in a future, the list view has to get acknowledge of that state to show something for example, the state should be in the parent component.
However, i think it's a thin line and you can do wathever makes sense in your specific case, I have a very similar case in my app and it's a simple case so i put the state in the child. Tomorrow maybe i must change it and put the state in his parent.
With many components depending on same state and its mutation you will encounter two issues.
They are placed in component tree so far away that your state will have to be stored in a parent component very high up in the render tree.
Placing the state very high far away from children components you will have to pass them down through many components that should not be aware of this state.
THERE ARE TWO SOLUTIONS FOR THIS ISSUE!
Use React.createContext and user context provider to pass the data to child elements.
Use redux, and react-redux libraries to save your state in store and connect it to different components in your app. For your information react-redux library uses React.createContext methods under the hood.
EXAMPLES:
Create Context
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
REDUX AND REACT-REDUX
import { connect } from 'react-redux'
const App = props => {
return <div>{props.user}</div>
}
const mapStateToProps = state => {
return state
}
export default connect(mapStateToProps)(App)
For more information about redux and react-redux check out this link:
https://redux.js.org/recipes/writing-tests#connected-components

Resources