Pass event through nested components bottom to top - reactjs

While this question has been asked before I did not find an answer. I have components nested to the level of great grandchild and I don't know how to get the data from the bottom to the top.
<Parent/>
<Child/>
<GrandChild/>
<GreatGrandChild/>
See an example: fiddle
The great grandchild is a form and I want the input data to get to the parent at the top. I had it working when it was just nested one level deep, but now that it is deeply nested it does not work. I'm not sure how to even pass the event up two levels.
I've heard using redux is possible but I wonder if there is a way to avoid it. Or, how do I avoid the nesting? Even through they are all actually separate components should I just move them into one big component? This might work but seems like bad practice?

Very simplified, you could just pass the function through all the components:
class GreatGrandChild extends React.Component {
render() {
return (
<div>
<input onChange={this.props.onChange}/>
<h2>I'm the GreatGrandChild</h2>
</div>
)
}
}
class GrandChild extends React.Component {
render() {
return (
<div>
<h2>I'm the GrandChild</h2>
<GreatGrandChild onChange={this.props.onChange}/>
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild onChange={this.props.onChange}/>
<h2>I'm the child</h2>
</div>
)
}
}
class Top extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
handleChildchange = (e) => {
console.log('child event on parent')
console.log(e.target.value);
}
render() {
return (
<div>
<Child onChange={this.handleChildchange}/>
<h2>I'm the parent</h2>
</div>
)
}
}
ReactDOM.render(<Top />, document.querySelector("#app"))

Redux is overkill for simple passing of props. You can pass props down through each child but it's easier to use the Context API like so:
Parent Component:
const MyContext = React.createContext('default');
export MyContext;
class Parent extends React.Component {
myFunction() {
//Do something here
}
render() {
return (
<MyContext.Provider value={this.myFunction}>
<ChildComponent />
</MyContext.Provider>
);
}
}
export default Parent;
Child Component:
import { MyContext } from './Parent';
class ChildComponent extends React.Component {
render() {
const { myFunction } = this.context;
return (
<div onClick={myFunction}>Click Me!</div>
);
}
}
ChildComponent.contextType = MyContext;
You can use the context as deep as you'd like, as long as you import it.

Simply pass a callback down from the parent via the props and make Sure it's passed all the way down to where you need it.

You also can pass props to your each child component in nesting and whenever values changed, you can call a parent function (nested) to get latest values in parent.

Related

Create a removable React component without having to re-create remove function

I am looking to create a "delete-able" / removable React component that I can use in multiple different places.
From researching, I can see it is kind of an anti-pattern to create a component that deletes itself and the correct way to do things is for the parent to manipulate the child components rather than child components modifying themselves.
This has led me to write code somewhat along the following lines:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: [ XXX ]
};
}
removeFunc = (index) => {
const test = this.state.data.filter((_,i) => i !== index);
this.setState({data: test});
}
render() {
return (
<div>
{this.state.data.map((el,i) =>
<ChildComponent removeFunc={() => this.removeFunc(i)}/>
)
}
</div>
);
}
}
export default ParentComponent;
class ChildComponent extends Component {
constructor(props) {
super(props);
this.state = {
removeFunc: props.removeFunc
};
}
render() {
return (
<div>
<button onClick={this.state.removeFunc}>Delete Me</button>
</div>
);
}
}
export default ChildComponent;
The issue I have with this is that I have to keep re-writing the removeFunc function in every parent component.
I am VERY new to React, so I'm just curious if there is there a better / different way to do this or is this the correct way?

How to get a componenet instance in React

I want to know if there is possible to get the component instance as I need.
I put my new component as a children in the main state, but is no the same object in both files.
I need to reach children state in my MainComponent. Looking in google for componenet instance doesnt help, maybe I am out of focus and the name of this is different.
Here is my MainComponent:
import React, { Component } from 'react';
import AnotherComponent from './../whatever/AnotherComponent';
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
children: [],
};
}
addChild() {
const { children } = this.state;
this.setState({
children: children.push(<AnotherComponent />)
});
}
getChildrenState(component) {
return component.state(); // this doesn't work!
}
render() {
const { children } = this.state;
return (
<div>
{(children.map(i => (<div key={this.getChildrenState(i).id}>{i}</div>))}
</div>
)
}
And This is AnotherComponent
import React, { Component } from 'react';
class AnotherComponent extends Component {
constructor(props) {
super(props);
this.state = {
id: 144,
};
}
render() {
return (
<div>
Here it is my cHild!
</div>
)
}
Putting <AnotherComponent/> to the state doesn't make sense because it's React element object that isn't associated with specific component instance.
Accessing children state in parent component breaks the encapsulation and indicates design problem.
The instance of class component should be retrieved with a ref, and doing so to access instance state is the last resort that may be needed to extend third-party components that don't provide desired functionality.
If AnotherComponent is first-party component, it should be designed accordingly, to not require state to be accessed from the outside:
render() {
return (
<div key={this.state.id}>{this.state.id}</div>
)
}
If the output needs to be more flexible, it can make use of render prop pattern:
render() {
const render = React.Children.only(children);
return (
<div key={this.state.id}>{render(this.state.id)}</div>
)
}
And used like:
<AnotherComponent>{id => <div>{id}</div>}</AnotherComponent>
If you want to access the state of the child component ( here AnotherComponent ) then you can either :
Maintain the state inside the AnotherComponent and pass the value to the parent ( here MainComponent ) on a change listener ( whenever the state changes ), or;
Maintain the state in the parent ( here MainComponent ) and pass the value to the child as prop.
Let me know if you want me to give an example implementation.

How to apply a HOC dynamically

I'm trying to apply the HOC to every child in my custom component. But I can't solve how to implement this for dynamic wrapped component type. Let say we have:
function myHOC<P>(WrappedComponent: React.ComponentType<P>):React.ComponentType<P> {
return class extends React.Component<P> {
...
render() {
return <WrappedComponent />;
}
const MyHOC = myHOC(???); //It won't do!
class MyComponent extends React.Component<Props, State> {
...
render() {
const items = this.props.children.map((child) => {
<MyHOC /> //I want to use it something like this!
});
return (
<div>
{items}
</div>
);
}
}
What do I need to add?
You can't apply HOC dynamically. If you want to use shared stateful logic dynamically you can think about render props pattern.

Generating Tags of existing components doesn't work in ReactJS

UPDATE
The number of the child comes from the parent, so i can't just load this in, this because i have a lot of components and i don't want import them all.
code has changed in the original post
ORIGINAL POST
I have a lot of components. I want dynamically generate the tags of some child components in a parent component. I looked for this and saw that this should work. But for some reason it doesn't..
codepen: https://codepen.io/anon/pen/qXPReP?editors=0010
class Child1 extends React.Component{
render() {
return (
<div>I'm Child1</div>
)
}
};
class Child2 extends React.Component{
render() {
return (
<div>I'm Child2</div>
)
}
};
class Child3 extends React.Component{
render() {
return (
<div>I'm Child3</div>
)
}
};
class Parent extends React.Component{
render() {
var LinkedTag = "Child" + this.props.childIdThatHasToBeRendered;
return (
<div>
<div>i'm the parent</div>
<LinkedTag />
</div>
)
}
};
class Main extends React.Component{
render() {
return (
<Parent childIdThatHasToBeRendered={3} />
)
}
};
ReactDOM.render(<Main />, document.body);
is this even possible to do it like this?
You need to dynamically pick a component to render...
You just need a host object to pick from
eg TagRenderers.js:
import Child1 from './child1'
import Child2 from './child2'
export default {
Child1,
Child2
}
... later
import TagRenderers from '../TagRenderers'
...
render(){
const TagRenderer = TagRenderers[`Child${this.state.number}`]
return <div>
<TagRenderer />
</div>
}
https://codepen.io/anon/pen/GvMWya?editors=0010
it's basically picking a property off an object - it will differ if you need to require the component just in time - this assumes they are all available in memory
Just remove your quotes
var LinkedTag = Child3;
Child3 is an object, not a string
updated codepen
You can show Child component based on condition like
class Parent extends React.Component{
render() {
let component = <Child3/> // Here you can add the code for condition.
return (
<div>
<div>i'm the parent</div>
{component}
</div>
)
}
};
In addition to #Dimitar Christoff, If you are using webpack code-splitting you can import those components dynamically so you don't have to include them all in your bundle for performance reasons.
Example:
// childComponent is the name of the component you want to import
const Render = require(`./${childComponent}.jsx`);
return (
// We add ".default" because its importing using the (require) function.
<Render.default />
);

Children to children components communication

I have 1 parent component name [Parent-1] and 1 child component name [Child-1]. Now, I have few more other components name [Other-1] and [Other-2].
Now i am passing [Other-1] and [Other-2] component to [Child-1] component. JSX is rendering correct. How can i access [Other-1/2] component functions from [Child-1] ? or How can i pass props to [Child-1] from [Other-1/2] ?
By using refs I am able to call the [Other-1/2] functions from [Parent-1] but I want to access from [Child-1]. I tried passing refs to [Child-1] like <Child abc={() => this.refs.other1.hello()}/> or <Child abc={this.refs.other1.hello()}/> but this is not working.
By using global Event Emitter way I am able to achieve solution to my above problem. But not sure if that's the appropriate way in React.js
I think you're not using the refs properly.
When you try to give an arrow function to refs it will sometime causes error due to the ref returning null. See my question to find out why.
The example should help you understand refs
Hope this helps!
class Others1 extends React.Component {
log(){
console.log('Hello from Others1')
}
render() {
return <h1>Others1</h1>
}
}
class Others2 extends React.Component {
log(){
console.log('Hello from Others2')
}
render() {
return <h1>Others2</h1>
}
}
class Child extends React.Component{
other1Ref(el) {
el.log()
}
other2Ref(el) {
el.log()
}
render() {
const Others1 = this.props.others1
const Others2 = this.props.others2
return (
<div>
<Others1 ref={this.other1Ref}/>
<Others2 ref={this.other2Ref}/>
</div>
)
}
}
class Parent extends React.Component{
render() {
return <Child others1={Others1} others2={Others2}/>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Additionally, There can be a case where we have to pass [Others1] and [Others2] as array of object say E.g.
class Others1 extends React.Component {
log(){
console.log('Hello from Others1');
}
render() {
return <h1>Others1</h1>
}
}
class Child extends React.Component{
other1Ref(el) {
el.log();
}
render() {
// 'i' need to be counter for eg. 0, 1, 2 ...
const Others1 = this.props._array[i].type();
Other1.other1Ref();
return (
<div></div>
)
}
}
let _array = [<Others1/>, ...];
class Parent extends React.Component{
render() {
return <Child _array={_array} />
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'))
By using .type() we will able to access Children function in case of array of objects/components

Resources