Add/Remove class to child component div from parent component using refs - reactjs

I want to add/remove class of child component's outer div from Parent Component on click of sidebar menu button(in parent component).
On click of SidNav toggle button ,expand the nav and add class to the Child Component's outer div so that the content gets pushed to right.
Tried : passing ref as props - I am getting the reference but actual child dom doesnot update when class applied from Parent Component.
Any suggestions would be helpful.
Snippet :
Parent Component :
return(<Child ref={this.ref} navigation={navigation} searchParams={parsedQuery} />)
child component :
function render() {
const { navigation, classlg2, ref } = this.props;
return (
<div className="bx--grid page-content--wrapper">
<div className="bx--row">
<div
ref={ref}
className={
navigation.length > 0
? "bx--col-lg-2 bx--col-lg-14"
: "bx--col-lg-16"
}
>
{routeComponentsElements}
</div>
</div>
</div>
);
}
bx--col-lg-2 is the class that I need to add and remove on toggle of SideNav.
If states are used to handle the className,then the content rerenders..
Example : there is ajax call in child component, when menubutton is clicked it rerenders the page going back to its home page.

Do something like this.
Parent component
this.state = {
childClass: false
}
toggleChildClass = () => {
this.setState(prevState =>({
childClass: !prevstate.childClass
}))
}
render(){
const {childClass} = this.state;
return(
<>
<button onClick={this.toggleChildClass}>Toggle Class</button>
<ChildComponent childClass={childClass} />
</>
)
}
Child Component
render(){
const { childClass } = this.props;
return(
<div
className={childClass ? "bx--col-lg-2 bx--col-lg-14"
: 'bx--col-lg-16'}>
{routeComponentsElements}
</div>
)
}

Related

Creating a parent 'workspace' component in ReactJS

Using ReactJS, I am trying to create a common workspace component that will have toolbar buttons and a navigation menu. The idea I have is to re-use this component to wrap all other dynamic components that I render in the app.
Currently, I've created a Toolbar and MenuBar components that I then add to each component in the app as such:
<Toolbar/>
<MenuBar/>
<Vendors/>
This does not feel right, since my aim is to have just one component which would be something like:
<Workspace>
<Vendor/>
</Workspace>
However, I am not sure of how to achieve this and whether this is the right approach.
As to whether or not it is the right approach is subjective, but I can provide insight into one way to make a "wrapper" type component:
// Your workspace wrapper component
class Workspace {
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
Toolbar goes here
</div>
<div className="workspace__nav">
Navgoes here
</div>
<div className="workspace__content">
{this.props.children}
</div>
</div>
)
}
}
// Using the component to define another one
class MyComponent {
render() {
return (
<Workspace>
This is my workspace content.
</Workspace>
)
}
}
You can also look at HOC's or Higher Order Components to wrap things.
React offer two traditional ways to make your component re useable
1- High-order Components
you can separate the logic in withWorkspace and then give it a component to apply that logic into it.
function withWorkSpace(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const Component = () => {
const Content = withWorkSpace(<SomeOtherComponent />)
return <Content />
}
2- Render Props
or you can use function props then give the parent state as arguments, just in case you need the parent state in child component.
const Workspace = () => {
state = {}
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
{this.props.renderTollbar(this.state)}
</div>
<div className="workspace__nav">
{this.props.renderNavigation(this.state)}
</div>
<div className="workspace__content">
{this.props.children(this.state)}
</div>
</div>
)
}
}
const Toolbar = (props) => {
return <div>Toolbar</div>
}
const Navigation = (props) => {
return <div>Toolbar</div>
}
class Component = () => {
return (
<Workspace
renderNavigation={(WorkspaceState) => <Navigation WorkspaceState={WorkspaceState} />}
renderTollbar={(WorkspaceState) => <Toolbar {...WorkspaceState} />}
>
{(WorkspaceState) => <SomeComponentForContent />}
</Workspace>
)
}

access method in child1 component from child2 component

Currently I have 3 components: Child1, Child2 and Parent. When a button in Child2 is pressed, I want to call a function in Child1 component.
This is what I have tried so far but I keep getting error saying
this.refs.child.child2Function is not a function
What would be the right way to handle such situation (calling a function in one child component from another child component)?
I am also using redux in my project. But my Child2 component is rendered in multiple tabs. So I cannot update the value using redux as the value will be updated throughout all the Child2 component.
Parent.js
class Parent extends Component{
parentFunction = () => {
console.log("This line gets printed. But the line below this breaks");
this.refs.child.child2Function();
}
render() {
return (
<View style={styles.pageContainer}>
<Child1 ref="child"/>
<Child2 parentFunc={this.parentFunction}/>
</View>
)}
Child2.js
class Child2 extends Component{
render() {
return (
<View style={styles.pageContainer}>
<Button
title="CLICK ME"
onPress={()=>this.props.parentFunc()} />
</View>
)}
Child1.js
class Child1 extends Component{
child1Function = () => {
console.log("Print this");
}
render() {
return (
<View style={styles.pageContainer}>
<Text>AAAA</Text>
</View>
)}
UPDATE:
Code works properly in the snack example set up here : https://snack.expo.io/rk4Y9RWUm
But I keep getting error when I have similar solution in my react native app. I have included screenshot of my code, console log and error message here : https://drive.google.com/drive/folders/1RQYicEC_wqHKPRCs9pVsMZyuTxyiUbq2?usp=sharing
You can not invoke a function inside child1 component directly from child2. Because the function inside child1 is encapsulated to child1 component it is not visible from outside. The only way to communicate between two components is through common parent component. You can declare required state and functions inside parent component and pass them as props to the child components. Child components then can share and change these states through functions passed from parent component. You can share state change between sibling component which share the same parent component. The only way of communication between sibling components is though common Parent component. Here is the link to sample code as an example: https://codesandbox.io/s/wom3622q8
Code sample:import React from "react";
const ChildOneComponent ({ number, handleChangeNumber }) => {
return (
<div>
<button onClick={handleChangeNumber}>Change Number</button>
State in ChildOneComponent is : {number}
</div>
);
};
const ChildTwoComponent ({ number, handleChangeNumber }) => {
return (
<div>
<button onClick={handleChangeNumber}>Change Number</button>
State in ChildTwoComponent is : {number}
</div>
);
};
class ParentComponent extends React.Component {
state = { number: 0 };
handleChangeNumber = () => {
let number = this.state.number;
this.setState(() => ({ number: ++number }));
console.log("cliked");
};
render() {
return (
<div>
<ChildOneComponent
number={this.state.number}
handleChangeNumber={this.handleChangeNumber}
/>
<ChildTwoComponent
number={this.state.number}
handleChangeNumber={this.handleChangeNumber}
/>
</div>
);
}
}

Reactjs focus an input field located in a child component which is inside another child component

I am currently trying to focus an input field that is located in a child component which is inside another child component. consider the following,
Parent.js
child1.js
child2.js
I would like to focus input field in child2 when a button is clicked from parent.js.
I don't say using this technique it's good but you can achieve this by creating a setRef function who get pass by the child 1 to the child 2. Make sure to read this https://reactjs.org/docs/refs-and-the-dom.html why refs is not the best thing. For me I would use props callback.
But if you really want to use ref this is how you can do. I put the code example here. You can also play with the code here https://codesandbox.io/s/5x8530j3xn
class Child2 extends React.Component {
render() {
return (
<div>
<input ref={this.props.setRef} />
</div>
);
}
}
class Child1 extends React.Component {
render() {
return (
<div>
<Child2 setRef={this.props.setRef} />
</div>
);
}
}
class Parent extends React.Component {
setRef = ref => {
this.input = ref;
};
focus = () => {
this.input.focus();
};
render() {
return (
<div>
<Child1 setRef={this.setRef} />
<button onClick={this.focus}>Go Focus</button>
</div>
);
}
}
Just use plain vanilla JavaScript inside a React method.
handleFocus () {
document.getElementById('inputField').focus()
}
<button onClick={this.handleFocus}>My Button</Button>
This will focus the input after clicking the button.

Get the child's props onClick in parent component

I have a parent ButtonGroup component and the child buttonItem component:
//ButtonGroup Component (Parent)
clicky(){
//capture the props of the clicked button, ie, caption and disabled here.
}
render() {
return (
<div onClick={this.clicky}>
{this.props.children}
</div>
)
}
//buttonItem component:
render() {
return (
<button disabled={this.props.disabled}>{this.props.caption}</button>
)
}
//final render
<ButtonGroupComponent>
<buttonItem caption="Nothing"/>
<buttonItem caption="Something" disabled={true}/>
<buttonItem caption="Refresh"/>
</ButtonGroupComponent>
from the above code is there any way i can capture the props of the clicked child buttonItem?
In your case, you need to merge this.props.children with your custom prop. So, I suggest you use React.Children to operate with it.
By the way, after adding new prop you need to return this child, so cloneElement will help you with this.
Inside import section of ButtonGroupComponent:
import React, { Children, Component, cloneElement } from 'react';
Its render function will look like this:
render() {
const childrenWithCustomHandler = Children.map(this.props.children, itemChild => {
// Arguments of cloneElement (component, [customProps], [customChildren])
return cloneElement(itemChild, { onClickItem: this.clicky })
}
);
return <div>{childrenWithCustomHandler}</div>;
}
And the code of buttonItem component will look like:
return (
<button
disabled={this.props.disabled}
onClick={() => {
this.props.onClickItem({ ...this.props });
}}
>
{this.props.caption}
</button>
)
I used Spread operator to clone the object, so if you will want to change props in your clicky function, the child won't be rendered.

Updating Child Component Via Change of Props from Parent in React

I have a component whose state is passed as a prop to a child component. I noticed that even if the Parent's state is changed, the child component is not re-rendered.
Based on the official React Documentation for utilizing shouldComponentUpdate:
"The first time the inner component gets rendered, it will have { foo:
'bar' } as the value prop. If the user clicks on the anchor, the
parent component's state will get updated to { value: { foo: 'barbar'
} }, triggering the re-rendering process of the inner component, which
will receive { foo: 'barbar' } as the new value for the prop.
The problem is that since the parent and inner components share a
reference to the same object, when the object gets mutated on line 2
of the onClick function, the prop the inner component had will change.
So, when the re-rendering process starts, and shouldComponentUpdate
gets invoked, this.props.value.foo will be equal to
nextProps.value.foo, because in fact, this.props.value references the
same object as nextProps.value.
Consequently, since we'll miss the change on the prop and short
circuit the re-rendering process, the UI won't get updated from 'bar'
to 'barbar'."
If I can't use shouldComponentUpdate, how would I force the child component to re-render based on a change of props from the Parent?
Parent Component
I want the child component to rerender based on the boolean given to showDropzone.
<Dropzone
onDrop={this.onDrop}
clearFile = {this.clearFile}
showDropzone = {this.state.filePresent}/>
Child Component
export default class Dropzone extends Component {
constructor(props) {
super(props);
}
render() {
const { onDrop } = this.props;
const {showDropzone} = this.props;
const {clearFile} = this.props;
return (
<div className="file-adder">
<div className="preview">
{{showDropzone}?
<Dropzone
onDrop={onDrop}
ref="dropzone">
</Dropzone>
:<button type="button"
className="btn btn-primary"
onClick={clearFile}>
Clear File</button>}
</div>
</div>
);
}
}
I'am sorry but your code has a lot of errors
First (This is a tip) inside:
const { onDrop } = this.props;
const { showDropzone } = this.props;
const { clearFile } = this.props;
You can write just one line
const { onDrop, showDropzone, clearFile } = this.props;
Now your problems starts from here:
You used showDropzone inside curly brackets and this is your
problem remove them
Your mistake {{showDropzone}? firstOption : anotherOption }
Will be { showDropzone? firstOption : anotherOption }
Another mistake:
You used the Dropzone component inside itself and this is a big mistake
You can't use Dropzone component here {{showDropzone}? <Dropzone {...}/> : anotherOption } You can use it from another component
Finally i tried to format your code to make it like this
Parent Component
{ this.state.filePresent
? <Dropzone onDrop={this.onDrop} />
: <button
type="button"
className="btn btn-primary"
onClick={clearFile}
children="Clear File"
/>
}
Child Compnent
class Dropzone extends Component {
render() {
const { onDrop } = this.props;
return (
<div className="file-adder">
<div className="preview" onDrop={onDrop} ref="dropzone">
<p>Hi! This is Dropzone component</p>
</div>
</div>
);
}
}
export default Dropzone;
In child component you have used "showZone" instead of "showDropzone".
Thanks

Resources