How do I pass React.createRef() to a child component? - reactjs

In my sidebar I have a list of radio buttons that should each select a component located in the main area of my app. I would like to set it so the component scrolls into view when I click the radio button that selects it, and as far as I can figure, I need to reference the component in order to scroll it into view, but I'm having trouble with this because I can't figure out how to properly pass a reference from child to parent component.
The parent component is a class component and in its constructor I set createRef() like this:
constructor(props) {
super(props);
this.myRef = React.createRef();
}
I then created a function that should be passed to child component and return ref:
getElem(ref) {
this.myRef = ref;
}
Now, inside the render of the parent component I pass the function to the child component like this:
<Designer
usedComponents={this.state.usedComponents}
myRef={this.getElem}
/>
Inside the Designer component I map the main area components and pass the ref to them like this:
const components = props.usedComponents.map((component, index) => {
return (
<div onMouseUp={() => props.selectComponent(index)} key={index}>
<DesignerComponent {...component} myRef={props.myRef} />
</div>
);
});
return components;
DesignerComponent component is determined through switch statement because each has a different set of props, but inside the switch statement I also pass myRef={props.myRef} to each individual component and then I set the ref inside each individual component like this:
<section ref={props.myRef}></section>
However, this breaks my app and gives me this error message:
"TypeError: Cannot set property 'myRef' of undefined".
What am I doing wrong?

I figured it out. The function getElem(ref) doesn't seem to do anything. I simply passed myRef={this.myRef} to the Designer component and now it works.

Related

How to scroll to a Ref within a modal scree in react

I currently have multiple components within a bootstrap modal. My goal is to be able to do a window.scroll to a given component following a user action. Basically, this is what I have tried:
class App extends Component {
constructor (props) {
super(props);
this.myref = React.createRef();
}
// function that I have been trying to invoke.
scrollToRef = () => {
window.scrollTo({top: this.myref.current.offsetTop, behavior: "smooth"})
}
render () {
return (
<Modal>
<ComponentOne/>
<ComponentTwo/>
<ComponentThree ref={this.myref}/>
</Modal>
)
}
}
All of my components are class components. I even tried wrapping ComponentThree in a div tag if that made a difference, but no luck. Any pointers would be greatly appreciated, Thank you!
window.scrollTo would pertain to the window object, therefore will attempt to scroll the window, not the <Modal> component. For this, you can have another ref attached to the <Modal>, and that is the element you would use scrollTo on. Since <ComponentThree> is already a direct child of <Modal>, you can continue using the offsetTop property but take note:
offsetTop is the number of pixels from the top of the closest
relatively positioned parent element

Strange problem using conditional rendering of component

I have a simple React component that takes in a single prop, if this prop is 0 show one component otherwise show another.
On page load it equals 0 so the first component loads, once my Redux store is updated from localstorage this value in the parent component updates its local state passing down the new prop to my component.
What happens is rather than switching to render my other component it puts the second component inside of the first... I have never seen this behaviour before and cannot for the life of me figure out why.
I have tried all sorts, switching between stateless and functional components, I have tried using componentWillReceiveProps to receive the updated prop and setState based on this and have my conditional logic based on state. All with the same results.
const ComponentThatGetsUpdated = (props) => (
props.PropThatGetsUpdated === 0 ? <ComponentOne /> : <ComponentTwo />
);
I expect on page load the output to be the contents of
<div>contents of component one</div>
then once my redux store has updated the new props are passed down the output should now be the contents of component two
<div>contents of component two</div>
The actual output once props have been updated is
<ComponentOne />
<ComponentTwo></ComponentTwo>
<ComponentOne />
FYI the parent component of ComponentThatGetsUpdated looks like the below:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {PropToPassDown: 0}
this.sub = this.sub.bind(this);
const sub = store.subscribe(this.sub);
}
sub() {
//when my redux store is updated this function runs and sets PropToPassDown to the new value, for this example it sets PropToPassDown in local state to 1
}
render() {
return (
<ComponentThatGetsUpdated PropToPassDown={this.state.PropToPassDown} />
)
}
}

Child component do not see Parent Component state updates

I'm new to React and i'm facing an issue related to state updates.
I have a Parent Component. In the Parent Component constructor, i create multiple instance of a Child Component.
Using the state of the Parent Component, i display one of the Child Component instance.
Instances of Child Component have some Parent Component state value passed as props.
The Parent Component state looks like this (i have simplify the code so it can be clearer)
displayedContainer: {...} // An Instance of Child Component
isLoading: false
The Parent Component constructor looks like this
constructor(props) {
super(props);
// Create state
this.state = {
isLoading: false,
displayedContainer: null
};
// Create all Child Component (Container)
this.defaultComponent = <Container
isLoading={this.state.isLoading}
></Container>
// Others Child Component are created the same way as above.
// To clearify the code i have removed them.
}
And here is the render method
render() {
return (
<div>
{this.state.displayedContainer}
<div className="container-left-bar"></div>
</div>
)
}
From there, i can switch from one Child Component display to another so the state.displayedContainer is working. But when the state.isLoading is getting updated, Child Components doesn't detect it. I think it's because i'm creating the Child Component in the constructor.
How should i do if i want to keep the logic of creating Child Components before rendered it but fix the issue of state updates not detected ?
Thanks for the help !
The problem is that you render the <Container /> only once, in the constructor. The rendered instance is in the memory (this.defaultComponent) and therefore when you call this.setState the child never gets updated - is not notified about the change of any prop. This code should go to render() method.
Think of it like this:
When React determines this.setState (e.g. you want to display other container then the current one), React calls render() method, and should rerender <Container .../> with updated props. But since the code for rendering the component is not in the render() method - code that tells the <Container .../> to use newest isLoading prop from the state, <Container /> never really gets updated with new value of the isLoading prop (or any other prop).
You should achieve something like this:
render() {
...
let renderCurrentContainer = null
if (...) {
renderCurrentContainer = <Container isLoading={this.state.isLoading} ...otherPropsHere... />
}
else if (...) {
renderCurrentContainer = ...
}
else if (...) {
renderCurrentContainer = ...
}
return <...>
{renderCurrentContainer}
</...>
}
If you're asking what to put into the if condition, you need to somehow mark which component to render currently, I'll leave that to your creativity, but you can use something like currentRenderedContainerIndex which can have values {0, 1, 2}, or currentRenderedContainer string from enum e.g. {'FIRST_COMPONENT', 'SECOND_COMPONENT', 'THIRD_COMPONENT'}
Then you would go with something like this:
if (currentRenderedContainer === 'FIRST_COMPONENT') {
renderCurrentContainer = <Container isLoading= {this.state.isLoading} ...otherPropsHere... />
}
else if (currentRenderedContainer === 'SECOND_COMPONENT') {
renderCurrentContainer = ...
}
else if (currentRenderedContainer === 'THIRD_COMPONENT') {
renderCurrentContainer = ...
}

Reactjs child component render

I have a parent component and child component called Card wrapped inside it.
Card is the third party component which render set of cards and has css transisioning affects in it like drag and change the layoout.
The issue is when the card change the layout i want to handle in parent component.
Currently card layout state is set in child which i dont have access.
How we can achieve this objective when child component layout change parent should get that change
You can pass the handle function from your parent to your child. Then, when an event on your child is triggered, you can do whatever you want (e.g. set state) on you parent component.
class Parent extends React.Component {
onChildLayoutChange = () => {
// do what you want to do with the parent component on child change here
}
render() {
return <Child onLayoutChange={this.onChildLayoutChange} />
}
}

how to Trigger componentWillReceiveProps on React-storybook

I am trying to test my UI component on React Storybook (link). However, I will like to add a button so when I pressed it, it will pass a new props to the component that is already rendered, (hence trigger the lifecycle method componentWillReceiveProps). However, I don't know how to do that. I will really appreciate any helps on this, thank you.
You need to find the first shared parent of the button and the component that you're trying to trigger componentWillReceiveProps for.
For instance, suppose your structure looks like this:
<SomeComponent>
<AnotherComponent someProp={someValue} />
<AThirdComponent>
<button />
</AThirdComponent>
</SomeComponent>
SomeComponent is the first shared parent of AnotherComponent and the button.
Once you've found your shared parent, add some state and a method to it:
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.updateButton = this.updateButton.bind(this);
this.state = { buttonWasClicked: false };
}
updateButton() {
this.setState({ buttonWasClicked: true});
}
}
Now pass the updateButton method down to your button in AThirdComponent and attach the state to AnotherComponent:
render() { // SomeComponent's render method
return (
<div>
<AnotherComponent buttonWasClicked={this.state.buttonWasClicked} />
<AThirdComponent updateButton={this.updateButton}`/>
</div>
);
}
And in AThirdComponent, attach the updateButton handler to the button:
<button onClick={this.props.updateButton} />
Now when you click the button, SomeComponent's state will update, causing it to pass new props to AnotherComponent.

Resources