I've been trying to wrap my head around this problem for a while. I've hacked together a solution that works, until I get any nested divs, then things fall apart. Basically what I'm trying to do is create composition components that live within a higher order component and all share the same current state. I then need to export that so that any file can use those components. So here's what the JSX might look like:
<Panel countersStartAt=5>
<Counter incrementsBy=1 />
<div>
<Counter incrementsBy=2 />
</div>
<TotalCounter className="someclass" />
</Panel>
So the way I want something like this to work is that I have this wrapper Panel component that sets some initial state, say this.state.start = 5. Within Panel, a Counter component would have an onClick handler that increments state.start by incrementsBy. And TotalCounter would be a component that displayed state.start. Of course this is a contrived example, so it would be helpful not to bring up how I could make this particular component better. I'm looking to apply this to a more realistic situation.
The second thing would be how to export those components in a way that I can create the exact code above in a separate file within a stateless component. Hopefully that makes sense.
This is a snippet of what I'm doing to achieve this.
renderChildren = (children) => {
return React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.createElement(
(child.type.name ? this[child.type.name] : child.type),
child.props
);
}
return child;
});
};
render = () => {
return (
{this.renderChildren(this.props.children)}
)
};
Then outside of the Panel class I'm exporting like so:
export const Counter = () => null;
Just so it exposes Counter. The default render of null doesn't happen because I replace Counter with the this.Counter() method within Panel.
Questions asked in Comments and Other things to consider
I am not using Flux or Redux
Assume that the Panel code snippet is used in several render methods across several projects that do not implement Flux patterns or Redux
Assume that those code snippets can't be re-written
How can can Panel, Counter, and TotalCounter be exported? Is it possible to do this for Counter and TotalCounter as they are methods within the Panel class? My research led to no, and creating "dummy" components to be exported so that the current file can use them without errors.
To put in an answer here for what we talked about in the chat room
the best way to handle what you want to do without a data management framework like Redux or Flux is to pass your data as props through, like so.
class Panel extends Component {
constructor(){
super()
this.state = {count: 5}
}
incrementCount = (incrementer) => {
this.setState({count: this.state.count + incrementer});
}
render (){
return (
<div>
<Counter incrementCount={this.incrementCount} count={this.state.count} incrementsBy=2 />
</div>
);
}
}
then in your counter..
<someElement onClick={ (e) => {this.props.incrementCount(this.props.incrementsBy)} }>{this.props.count}</someElement>
Related
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.
I have a chess game I am building with React-Redux...
The issue I am having is how to highlight the squares available for a piece to move to with my current architecture.
I've tried passing data from the children to the parent via props, but this doesn't seem to work. I've also tried using refs and ran into a lot more goofy issues. I think I am missing something obvious here...
<Board/>
is made up of an array of 64:
<Square key={} />
components. Each has a key prop that is a chess square ID, like "a8" or "f6".
I have a method of my App component that returns an array of available squares to move to when one clicks a piece.
calculateAvailableSquares(pos, piece, board){
//returns an array like ["f5", "f4","f3"]
//TODO get this function to highlight the <Square/>s
/// that have a key within the returned array
}
How can I fire a method in my App component and pass an updated className or style prop to all and only the Square components who's keys are in the array?
I don't understand how to select these children in React, and I don't understand how, once I would have them selected, I would alter them dynamically like this.
You mentioned you are using Redux, so you could set your global state of which squares are highlighted by calling one of your Redux reducers.
Alternatively, a quicker and easier way to do it would be to pass a function from the parent component to all the child components.
class ParentComponent {
state = {
highlightedSquares: []
}
setSquareState = square => {
const highlightedSquares = this.state.highlightedSquares.push(square)
this.setState({ highlightedSquares })
}
render() {
<div>
<Square setSquareState={this.setSquareState} />
</div>
}
}
const Square = props => (
<div className={props.isHighligted ? "highlighted" : ""}>
<button onClick={props.setSquareState}>Highlight</button>
</div>
)
I am still fairly new to Reactjs and I am struggling with one thing.
I have built a small React app that consists of:
App.js at the very top –the only class component that controls all the states
3 stateless components ( no1 , no2, no3 rendered in App.js (all with smaller components inside them, but that’s irrelevant)
App.js state includes 3 arrays all of which are used by those 3 stateless components rendered.
And then, in App.js I have around 400 lines of methods where I am modifying state by calling setState. That’s quite long....
My question is: is there anyway to split this file? To move methods to their respective components: no 1, 2 and 3.
It seems impossible to me as having all the states in one class requires calling setState (having setState methods) in the same class only.
This might be a stupid question but:
Is it possible to modify state outside parent that holds this state (App.js), for instance, modify it in a stateless component no 1, and still keep parent updated about the change so that it can inform stateless components no 2 and no 3 about the change.
What’s the best practice in my case?
thanks
What i do for code/logic splitting is:
Keep the App.js as simple as possible
If there is a lot of logic in a file/Component, i split the file to index.js and Component.js and keep the logic in index.js and the JSX/HTML in the Component.js
In your case you can:
Remove all logic from App.js
Create 3 folders no1, no2 and no3
Inside each folder create two files - index.js and ComponentNo1.js etc
In the index.js create a Component with state and in ComponentNo1.js create a stateless component that returns the JSX/HTML.
This is what I would recommend. If you find it hard to do it, post your App.js logic so that I could help you do it.
Sure something like this is completely valid and is a common React practice.
I've written a sandbox to give you an idea of how this works:
https://codesandbox.io/s/zxl2owp2np
But to explain in detail, let's assume you have the following components:
class App extends React.Component {
state = {
value: 5
};
increaseValue = () => {
this.setState({
value: this.state.value + 1
});
};
render() {
return (
<div className="App">
<Example1 value={this.state.value} increaseValue={this.increaseValue} />
<Example2 value={this.state.value} />
<Example3 value={this.state.value} />
</div>
);
}
}
const Example1 = (props) => {
const value = props.value;
return (
<div>
I am Example1. The value is {value}
<button onClick={() => props.increaseValue()}>Click</button>
</div>
);
}
const Example2 = (props) => {
const value = props.value;
return (
<div>
I am Example2. The value is {value}
</div>
);
}
const Example3 = (props) => {
const value = props.value;
return (
<div>
I am Example3. The value is {value}
</div>
);
}
Our main App component has a state value of 5 that we pass into our 3 child components. The child components are all using that value to display data. In the App component, we defined a state-updating function called increaseValue, which we pass in to Example1 as property. Now Example1 can use increaseValue by calling props.increaseValue()
props.increaseValue() is bound to our App component's execution context. So it will update the value in App's component state. Now with the updated value, it gets shared across all our Example components for use.
Regarding your question about moving methods to the components that are using them. This is not a common practice. Typically you define the function inside the component where the state is going to be updated. Meaning, if you have a function that is meant to update the App component, it should be defined in the app component.
I am looking to validate a form with Redux. I am trying to use make a form component which will iterate through children and find various input components (not to be confused with a native <input>.
I know there are a lot of open source solutions, but I'd like to understand some mechanics before jumping into picking any. I have a Form component setup to test like this:
import React from 'react';
export default class Component extends React.Component {
componentDidMount() {
this._iterate(this.props.children);
}
render(){
return (
<form {...this.props}>{this.props.children}</form>
);
}
_iterate(children) {
React.Children.forEach(children, child => {
console.log(child);
if (child.props.children) {
console.log('get children');
this._iterate(child.props.children);
}
});
}
};
I then have another Component with a render like this:
render() {
return (
<div>
<Form>
<ComponentA />
<ComponentB />
</Form>
</div>
);
}
Now ComponentA or ComponentB might have a component that nests more components down the line. Within those components would be a React component I have made for Text, Select, etc.
The code above would just console.log the components, and any children of them, that are in this specific render. It does not jump down into ComponentA children.
Is there a solution to that?
This isn't a problem you really want to solve.
The power in react is largely around the design pattern it encourages, and what you're doing is breaking that pattern; Component's should only talk to their immediate children and respond to their immediate parents. If you need to go deeper than that, then the component in the middle needs to be responsible for passing that data.
Rather than trying to dig into the innards of ComponentA and ComponentB, those component's themselves should have the accessibility props that you need. I.e., <ComponentA onChange={whatever} errorMessage={whatever}/> etc. and then hooking those props to their children should occur within ComponentA.
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