How to get a componenet instance in React - reactjs

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.

Related

Props are empty on passing them to child class based components while filled when using functional component

It seems that props are not being passed to child component when my child component is a class based component. This is my parent component:
class ScreeningsList extends React.Component{
state = {screenings: [] };
componentDidMount = async () => {
const response = await apis.getAllScreenings();
this.setState({screenings: response.data});
}
render(){
return (
<div>
<ScrTest screeningsList = {this.state.screenings}/>
</div>
)
}
}
And here is my child component:
class ScrTest extends React.Component{
render(){
return (
<div className="screeningsContainer">
<h2>Title: {this.props.screeningsList._id}</h2>
</div>
)
}
}
Here I tried just to display id of the screening. The value in props did not exist. There were no props at all.
However when I use functional component and use the arrow function:
const ScreeningsToRender = props => {
...
}
I can access props by using props.screeningsList and use every value that is inside that prop. Rendering the component is succesfull and I can see the list of all the screenings. What should I do to recieve props properly in my child component?
You need a constructor in the child component:
class ScrTest extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div className="screeningsContainer">
<h2>Title: {this.props.screeningsList._id}</h2>
</div>
)
}
}
"When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor".
Source: https://reactjs.org/docs/react-component.html#constructor

Can a React component render another component stored in it's state?

I tried to create a container component that might change its inner component over time.
class Container extends React.Component {
constructor(props) {
super(props)
this.state = { content: new FooComponent() }
}
render() {
return <div>
{ this.state.content }
</div>
}
}
I get errors like:
Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state}). If you meant to render a collection of children, use an array instead.
I tried { [this.state.component] }, but get same error.
Are you able to put components from this.state into the JSX that is returned from render()? If not, how do you implement a container? I'm thinking of creating a container something like iOS's UINavigationController, where I can push and pop components in a stack, with only the top one rendered to the screen.
You can use JSX: this.state = { content: <FooComponent/> }
Which is React.createElement() behind the scenes.
class Container extends React.Component {
constructor(props) {
super(props)
this.state = { content: <FooComponent/> }
}
render() {
return <div>{this.state.content}</div>;
}
}
Or refer to Creating React Elements.
For reference here are more readable forms:
class Container extends React.Component {
state = {
content: <FooComponent />
};
render() {
return <>{this.state.content}</>;
}
}
function Container() {
const [content, setContent] = useState(<FooComponent />);
return <>{content}</>;
}
Can a React component render another component stored in it's state?
Yes you can!
There is two ways of doing it
1
import FooComponent from '..'
...
this.state = {
content: <FooComponent/>
}
...
render(){
return (
<div>{this.state.content}</div>
)
}
or
2
import FooComponent from '..'
...
this.state = {
content: FooComponent
}
...
render(){
const {
content: BarComponent
} = this.state
return (
<div>
<BarComponent />
</div>
)
}
I get errors like:
Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state}). If you meant to render a collection of children, use an array instead.
This happens because when you do new FooComponent() it returns an object and you can't render an object.
When using JSX, will be transpiled to React.createElement(FooComponent) and this is renderable.
When using only FooComponent, you store a reference to it and only in the render method you create a JSX that will become React.createElement(BarComponent)
The second aproach is good when the component is comming from props and you depending on the situation, you want to give it some props.
Working Example
The main question probably already answered there.
Notice: Limited functionality - storing only components will be a simple stateless view/screen stack/buffer.
The problem can be much wider. This solution won't be equal to saving previous states, changes ... no props/params, no history, no context, state reversing ... but shared common state (redux store) can be an advantage - no old states, autoupdating the whole chain of stored views.
class Container extends React.Component {
constructor(props) {
super(props)
this.state = { content: this.FooComponent() }
}
FooComponent(){
return(<FooComponent/>)
}
render() {
return <div>
{ this.state.content }
</div>
}
}

Pass event through nested components bottom to top

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.

Does React allow getting or setting props outside of components

When I have a component inside a component
var MySubComponent;
class MyComponent {
...
getChildProps() {
console.log(MySubComponent.props.nameToGet)
}
...
MySubComponent = withCustomAudio(props => {return(<div>...</div>)});
...
render() {
return(...<MySubComponent {...this.props}/>...)
}
...}
After component render I'd like to get or set the prop of the subcomponent. It returns cant access nameToGet of undefined
Spread attributes is not what I want
(https://zhenyong.github.io/react/docs/transferring-props.html)
No, you won't be able to get/set props like this. It's not the React way. You should read more about thinking in React.
Basically, if you want to change child props in the parent, they should be part of the parent state and passed down to the child as props.
Example:
import React, {Component} from "react";
class Parent extends Component {
state = {
childProp: "Not changed yet"
}
handleChildPropChange = () => {
this.setState({
childProp: "I changed"
})
}
render() {
return <Child myProp={this.state.childProp} onChangeChildProp={this.handleChildPropChange} />;
}
}
class Child extends Component {
render() {
return (
<div onClick={onChangeChildProp}>{this.props.myProp}</div>
)
}
}

How to use Higher Order function (or alternative) for wrapping a React component without the component continually unmounting?

I have a React component (Parent) within which there is another componet (Child) that is wrapped using a Higher Order function (HOWrapper). The problem I am having is that each time the Parent component renders, the Child component unmounts and then re-mounts. I need to find a way that prevents the Child component from unmounting, but still continue to wrap it in a component whose name is dynamically assigned. I also want the option to pass additional parameters to the HOWrapper function, that may also be dynamically generated in the render() function of Parent. Any ideas?
Parent component:
import { HOWrapper } from './somepath';
import Child from './someotherpath';
export default class Parent extends Component {
...
render(){
let ChildWrapper = HOWrapper(Child);
return (
<ChildWrapper {...this.props} />
);
}
}
Higher Order function:
export function HOWrapper(WrappedComponent) {
return class Blanket extends Component {
constructor(props) {
super(props);
this.state = {
...
};
}
...
render() {
return (
<WrappedComponent
{...this.props}
/>
);
}
}
}
Because an HOC returns a component, a more typical approach to using them is once when exporting them, not on every render call.
let YourComponent = props => <div />
export default HOC(YourComponent)
Then if you want to do anything dynamic pass new props to it.
render() { return <YourComponent dynamicProps={foo} /> }

Resources