Calling this.refs.component.function - reactjs

So i have a navigation component at a higher level like this:
<Navigation ref="nav">
<Children>
<Footer>
In one of my children components I want to call a method inside my Navigation component. How would I do so? I gave this a shot:
if Navigation has a method called
toggle() {
this.setState({open: true})
}
could I call it like this in the children component?
Children:
openSomething = () => {
console.log(this.refs);
this.refs.nav.toggle();
}
<Somebutton onClick={openSomething} name="OpenStuff" />
After i had done this, refs had come out undefined, so I wasn't sure how to go about this. The idea is that i have a child component that I want to open a modal on button click, but it is its child and I thought of doing it this way, however it didnt work.

You can't do this from Children because Navigation is its sibling. Only their parent can refer to Navigation via refs. You can trigger an event from Children which is handled by the parent:
<Somebutton onClick={this.props.onOpen} name="OpenStuff" />
And then handle it in the parent component:
render() {
return (
<div>
<Navigation ref="nav" />
<Children onOpen={() => this.handleOnOpen()} />
</div>
)
}
handleOnOpen() {
this.refs.nav.toggle()
}

Related

Component Re-rendering issue

I am working on the project in React Typescript.
I have created hierarchy of components as per requirement.
In one scenario I have to pass data from child component to parent component and I am passing function as props and it works.
Issue :
When passing data to parent component child component gets re-render it looks like. Mean to say Dropdown selection is get reset and tree control expanded nodes get collapsed and set to the position as first time rendered.
I have used useState,useEffects hooks.
I have also tried React.memo as a part of my search on internet.
What I need :
I want to pass data to parent component from child without re-render the child component as there is no change in the props of child component.
Try this approach:
Add useCallback hook to memoize your function which lift data to <Parent />.
Then use React.memo for <Child /> to control prop changes and avoid unwanted re-renders.
I prepare an example for you here.
UPD. I have uploaded an example, you can copy it and see how it works!
Here is Child component:
const Child = ({ onChange }) => {
console.log("Child re-render");
return (
<div className="App">
<h1>Child</h1>
<button onClick={() => onChange(Math.random())}>
Lift value to Parant
</button>
</div>
);
};
const areEqual = ({ onChange: prevOnChange }, { onChange }) => {
return prevOnChange === onChange; // if true => this will avoid render
}
export default React.memo(Child, areEqual);
And the Parent:
consn App = () => {
const [value, setValue] = useState("");
const onChange = useCallback((value) => setValue(String(value)), []);
console.log("Parant re-render");
return (
<div className="App">
<h1>Parent</h1>
<div>Value is: {value}</div>
<Child onChange={onChange} />
</div>
);
}
Best regards 🚀

React layout children dynamically without unmounting

I have a React component that is used to layout its children according to their key. Here is what it looks like:
<Container focusedChildKey={this.state.focusedChildKey}>
<Child key="child1" />
<Child key="child2" />
<Child key="child3" />
</Container>
In the container, in the render function I do something simple:
let focused = null;
let normals = [];
React.Children.forEach(children, (child) => {
if (child.key === this.props.focusedChildKey) {
focused = child
} else {
normals.push(child);
}
});
return (
<div>
<div className="focusedChild", style={{ ... }}>
{focused}
</div>
{normals}
</div>
);
The problem I encounter is that whenever the focusedChildKey props changes on the Container, the focused child got unmounted and mounted again. Children in my case carry a lot of states that I lose every time one of the child get focused.
I read a lot but can't understand why it's re-mounting the whole component.

How to set one component's state from another component in React

Say I have the following component named Home:
this.state = {
posts: []
}
componentDidMount() {
... data is fetched from api
}
<div
<PostForm />
{this.state.posts.map(post => (
<Post key={post.id} post={post} />
))}
</div>
How would I update the state of Home, or re fetch data from the api after the the PostForm component is submitted with a new Post.
Welcome to SO!
Setting the parent state from the child:
If you want your child component to have access to your parent component's state, just pass setState() as a prop in your parent class, like so...
<PostForm
setParentState={(state) => this.setState(state)}
/>
Then, later in PostForm.js, just set the parent state like so....
this.props.setParentState(newParentStateObject);
Or, you can even just do....
<PostForm
postform={this}
/>
And later on, you can call anything in postform with this.props.postform.anyFunctionEver().
Setting the child state from the parent:
Suppose you want to do the opposite now: update the state of the child component from the parent? That's just as easy, set a reference when defining <PostForm/>...
<PostForm
ref={(instance) => {this.postform = instance}}
/>
Then you can set the postform's state directly in your parent class...
this.postform.setState(newChildStateObject);
A lot can happen with the state, so if you're not sure, try making a testFunc() {console.log('test');}, and then try passing/activating this between parent and child.

lazy render children

Take a look at this simple example:
const List = function({ loading, entity }) {
return (
<Layout loading={loading}>
<span>Name: {entity.name}</span>
</Layout>
);
};
Layout component is rendering its children only when loading is false. But the problem here is that React is resolving Layout children immediatelly. Since entity is null (while loading=true) I get error that it can't read name of null. Is there a simple way to avoid this error since this span will always be rendered when entity is not null?
Currently I know about 3 options:
Move this span to stateless function which receives entity as prop
Wrap whole children of Layout in function and then support function children in Layout
Just use {entity && <span>Name: {entity.name}</span>}
Why do I have to use one of these options and can I make React to consider those children as function and resolve block inside later on before the render?
I just stumbled upon the same problem.
What worked for me was passing children as a function:
ready: boolean;
children?: () => ReactNode,
}> = ({ ready, children }) => {
return (
<div>{
ready ?
children() :
(
<div>not ready</div>
)
}</div>
);
};
<Layout loading={loading}>
{() =>
<span>Name: {entity.name}</span>
}
</Layout>
Though it's still not perfect.

Pass JSX to child, and have the child edit JSX props?

How can <MyList> add prop myListProp={true} to <SomeComponent1>?
I want MyList to manage props, and not the class where this JSX is declared (theCurrentClass)
<div className='theCurrentClass'>
<MyList>
<SomeComponent1 />
<OtherComponent2 />
<WowComponent3 />
</MyList>
</div>
Is cloneElement the only way?
I need to constantly update these props / styles, and don't want to continuously clone the JSX.
This does seem to be accounted for, both with cloneElement and JSX.
https://facebook.github.io/react/docs/react-api.html#cloneelement
return this.props.children.map((child, index) => {
return React.cloneElement(child, {
...child.props,
});
});
return this.props.children.map((child, index) => {
return ( <child.type {...child.props}></child.type> );
});
I can interface with the element by adding props the same way I do with any rendered element
NOTE: requestAnimationFrame loops should only edit the ref (passed back from the child in a callback on didMount), and rarely update props unless you want to deal with all the React "smart" analysis bogging down the 60fps

Resources