React access one components function from another - reactjs

I am building a react app and have two functional components which do not have a parent/ child relationship.
Component one renders a canvas element and has functions which alter that element.
Component two is for the UI and has buttons which I want to trigger component one's functions.
The app is quite complex and I want to keep all the canvas functions in one place not in the global app scope.
My question is how do I reference component one functions in component two.
Component one:
export default function CanvasElement() {
let drawImage = () => {
/* Alter canvas */
}
return (
<div>
<canvas id="image-region-canvas"></canvas>
</div>
)
}
Component two:
export default function UIElement() {
return (
<div>
<button onClick={canvasElement.drawImage}></button>
</div>
)
}
App.js:
class App extends Component {
render() {
return (
<div className="card-designer">
<CanvasElement/>
<UIElement/>
</div>
);
}
}

You actually need to shift the drawImage() function up to the App component and pass this as props to both or essentially UIElement.

You should treat <App> like a container, or wrap both child components in a container parent inside <App>. Then, place the functions in the parent and pass what they return as props to CanvasElement. That way you can call the functions using button clicks and pass the result to the other child.

Why don't you use a container component?
export default function Container() {
let drawImage = () => {
/* Alter canvas */
}
return (
<div>
<CanvasElement/>
<UIElement drawImage={drawImage}/>
</div>
)
}
To better re-usability you have to use both presentation components and container components.
All the business logic or data manipulation needs to handle by container components. Presentation components are only responsible for displaying the data that get from props.
So with the above implementation, you can keep all your business logic data in one place (In this case, inside the container component) Then you can pass down any necessary things as props to child components.

Related

How to deal with the props and tightly coupled components in React?

I have a couple components which are tightly coupled to each other. The highest component receives prop called options. The prop options is passed down through next components and so on.
Which is the best way to emit changes from nested components to each others? I wouldn't rather use redux in this case.
This example will work for React16.3 and above.
Click here to check working example.
a) Get Data from parent component to nested chid conponent using context api of react
1. Grand Parent Component
Context lets us pass a value deep into the component tree without
explicitly threading it through every component. Create a context for
the current theme (with "light" as the default).
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.
const theme = "dark";
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
}
2. Parent Component
A component in the middle doesn't have to pass the theme down
explicitly anymore.
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
3. Child Component
function ThemedButton(props) {
// Use a Consumer 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".
return (
<ThemeContext.Consumer>
{theme => <div>{theme}</div>}
</ThemeContext.Consumer>
);
}
Replace theme to options in your case.
For more detail take the reference of react doc. Click here
b) Store data from parent component to store and get it in nested child component using redux
Here you are getting data from state and passing options data to
your component
const mapStateToProps = (state) => ({
options: state.options,
});
Here you are connecting your component from state
export default connect(
mapStateToProps,
null,
)(ChildComponent);

React: Nested Reusable Composition Components within a Higher Order Component

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>

How can I find all nested Components using React/Redux?

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.

How to communicate from Child Component to Parent Component with React Router

I have a main component App containing some children according to the routes (I use react-router) etc :
class App extends Component {
otherClick = () => { /* run every children's `handleButton2` function */ }
<div className="App">
<Button handleMenuClick={this.toggleSideBar}>Button 1</Button>
<Button handleOtherClick={this.otherClick}>Button 2</Button>
<SideBar ref="sideBar" title="Toto"/>
{this.props.children}
</div>
}
So, according to the route, App will contain some other containers such as:
class ContainerABC extends React.Component {
constructor(props) {
super(props);
}
handleButton2 = () => {
let sc = this.refs.subCont;
sc.setState({visible : !sc.visible});
// Change the color of Button 2 ???
};
render() {
return (
<div>
<SubContainer ref="subCont"/>
</div>
);
}
};
The role of Button 2 depends on the current Container. In the example above, when I have a ContainerABC as child, I want that Button 2 toggles the SubContainer of ContainerABC.
How can I tell to Button 2 to do the appropriate action according to the child of the component ?
And/or how can I modify Button 2 (or any trigger) from SubCont when Button 2 triggers an action on SubCont ?
Maybe using Redux ? I don't see how it could be helpful
Redux might help only because it can trigger an action that, in return, modifies the global state tree (e.g. redux store through a reducer). If that's the only purpose you need fulfilling, then I'd recommend against adding complexity (as much as I fancy Redux).
I assume you want a random child from {this.props.children} fire a random action once Button 2 is clicked?
Let's observe this commonly enforced React pattern:
Properties flow downwards. Actions (read: callbacks) go upwards.
That said, you may want to iterate through your {this.props.children} and check for the existence of a special callback prop that adheres to your API requirements.
React.Children.forEach(this.props.children, (child) => {
if (typeof child.props.toggleButton2State !== "function") {
throw('Woah, cowboy, you need that toggleButton2State function);
}
}
Then your button could cycle through children in the same manner and execute that function, if exists.
handleButton2Click() {
React.Children.forEach(this.props.children, (child) => {
if (typeof child.props.toggleButton2State === "function") {
child.props.toggleButton2State.call(child, !oldState, this);
}
}
}
So you just called child's callback function in scope of the child with boolean state being toggled and you also passed the reference to the parent component (this).
I would strongly suggest you never manipulate the parent container from a child. You never know how your hierarchy may change.
Obviously, this is a very rough example but it should get you going. Let me know how it goes.
If the behavior of the button depends on what container is being rendered, then it sounds to me like the container should render the buttons. You could wire up some props (could even use cloneElement to put them on the children) so you can pass callbacks down that would change the behavior of the button, but that sounds like a nightmare to maintain.
You could put those buttons in a separate component (with a prop to determine what they do) and render it in the containers. That sounds much simpler to me.

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