Can Father component show child component state ?? - reactjs

DragAndResizeBox.js
I use ref={(c) => { this.resizableandmovable = c; }} in ResizableAndMovable as the react-rnd document said
I found I can get this.resizableandmovable.state.x ,this.resizableandmovable.state.y
class DragAndResizeBox extends Component {
constructor(props) {
super(props);
}
onDragStop = (event, ui) => {
console.log("[onDragStop]state:",this.resizableandmovable.state)
}
render() {
const { canDrop, isOver, connectDropTarget } = this.props;
const isActive = canDrop && isOver;
const { contentObjectId, boxType } = this.props
return connectDropTarget(
<div>
<ResizableAndMovable
ref={(c) => { this.resizableandmovable = c; }}
initial = {{
x:this.props.axisX ? this.props.axisX: 0,
y:this.props.axisY ? this.props.axisY :0,
width:this.props.width ? this.props.width: 200,
height:this.props.height ? this.props.height: 200,
}}
onDragStop={this.onDragStop}
>
<div className={this.getCssStyle(boxType)}>
{this.show(contentObjectId)}
</div>
</ResizableAndMovable>
</div>
);
}
}
But I want to show the position x,y
How can I get the this.resizableandmovable.state in father component ?
**father component **
import DragAndResizeBox from './DragAndResizeBox';
boxes.map(box => {
return (
<DragAndResizeBox key={box.id} />
);
});
// Can I directly access this.resizableandmovable.state ???
//<div>X:<textarea>{this.resizableandmovable.state.x}</textarea>Y:{this.resizableandmovable.state.y}<textarea></textarea>

Short answer is no you can't.
What you can and should do is to store state in the parent component. And pass down to child component via props.
Updating function also normally sits inside parent component and passed down as props to the child.

Related

Is there a way to refresh a childs state from a parent component (Both are function components)

function clearChildState(){
//Stuff to clear child state from parent components
}
I want an input from the user (Who sees the parent component) to click a button to clear child components states. How would one go about this?
You can pass the items as prop (parent->child).
<Child items={items} />
The child continues to have an items state, which is initialized from the items prop.
When the parent passes an empty array to the child, the child's items state would be reset to [].
This can be achieved using getDerivedStateFromProps in class based child component.
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {items: []};
}
static getDerivedStateFromProps(props,state) {
return {items: props.items};
}
// render
}
If you must use function components, you could use refs and effects to do it like this:
const ChildComponent = (props) => {
// Store your initial state
const initialState = {
name: 'Default Name',
title: 'Default Title'
}
const [{name, title} , setState] = React.useState(initialState)
const setNewState = () => {
setState({ name: 'New Name', title: 'New Title' })
}
// Use a ref to store the previous prop value
const prevClearChildStateCount = React.useRef(0)
// Use effects to run a check whenever the prop is updated
React.useEffect(() => {
if (props.clearChildStateCount > prevClearChildStateCount.current) {
setState(initialState)
}
prevClearChildStateCount.current = props.clearChildStateCount
}, [props.clearChildStateCount])
return (
<div>
<p>Name: {name}</p>
<p>Title: {title}</p>
<button onClick={setNewState}>Set new state</button>
</div>
)
}
const ParentComponent = () => {
const [clearChildStateCount, updateClearChildState] = React.useState(0)
const clearChildState = () => {
updateClearChildState(clearChildStateCount + 1)
}
return (
<div>
<ChildComponent clearChildStateCount={clearChildStateCount} />
<button onClick={clearChildState}>Clear Child State</button>
</div>
)
}
ReactDOM.render(<ParentComponent />, document.getElementById('container'))
Link to Fiddle

How can I pass a prop from child to parent component

How can I pass a prop (when modified) from Child Component to a Parent Component.
Some Details :
I am working on an existing codebase where I have Parent Component embedded in
'unstated.Container' and a separate Child Component , where I am trying to add a button. When
a user clicks this button some value gets updated , which needs to be passed to the Parent
component .
Thank you.
import {Container} from 'unstated';
class DelContainer extends Container{
state = { sortAsc : true, notified : null}
setVal = async (Id, value) => { console.log (`Id : ${Id}); console.log('value: ${value}); }
}
//Child Component (Separate file)
const ChildItems = (props) => {
const [some_value ] = props;
const [some_color, setColor] = useState(" ");
const MarkIt = ({some_value})
{
some_value = this.props.some_value; //ISSUE HERE
}
return (
<IconButton >
<StarOutlinedIcon onClick = {MarkIt} style={{color: `${some_color}`}}/>
</IconButton>
);
}
//Parent Component (Separate file)
import {Subscribe} from 'unstated';
const DelList = (props) => {
return(
<Subscribe to ={[DelContainer]}>
{
(delStore) => {
const[person, isLoading] = delStore.state;
return(
<div>
<List className = {props.className} isLoading = {Loading}>
{
isLoading && person
.map((person, index)=>{
return <ChildItem key={index}
person = {person}
some_value = {delStore.MarkIt(some_value)};
}
}
</List<
</div>
)
}
}
);
}
Read this :
How to update parent's state in React?
Reactjs DOCS:
https://reactjs.org/docs/lifting-state-up.html
class Parent extends React.Component {
liftStateHander=()=> {
this.setState({
name:"John"
})
}
render() {
return <Child handler={this.liftStateHander} />
}
}
class Child extends React.Component {
render() {
return (
<button onClick={this.props.handler}>
Click For Change State Parent
</button>
)
}
}

Reuse same DOM element in two different react components

I have a small question.
Let's imagine I have component A which holds , after component A does it's job I render component B. I would like that same DOM element (textarea) would be reused in component B.
The reason is if new textarea is rendered in component B it loses focus as it's just new DOM element. It's like after component A lifetame take textarea element from it and just put it in component B instead of rendering new one.
Sample APP
https://jsfiddle.net/remdex/v67gqyLa/1/#&togetherjs=aiRvTGhRK2
class AComponent extends React.Component {
render() {
return ( <textarea>A Component</textarea> )
}
}
class BComponent extends React.Component {
render() {
return ( <textarea>Should be A Component text</textarea> )
}
}
class ABComponent extends React.Component {
constructor(props) {
super(props)
this.state = {'component' : 'A'};
}
render() {
return (
<div><button onClick={(e) => this.setState({component:'B'})}>Switch to B Component</button>
{this.state.component == 'A' && <AComponent/>}
{this.state.component == 'B' && <BComponent/>}
</div>
)
}
}
ReactDOM.render(<ABComponent />, document.querySelector("#app"))
In your sandbox example, ComponentA and ComponentB are redundant. You can create ComponentA and ComponentB as a class if they are using same element and operate them with ComponentAB. You can change your ComponentAB like:
class A {
handle(input) {
// Do your A job here and return result
console.log("Handler A is running");
};
}
class B {
handle(input) {
// Do your B job here and return result
console.log("Handler B is running");
};
}
class ABComponent extends React.Component {
currentHandler = new A();
handleClick = () => {
this.currentHandler = new B();
};
handleChange = (event) => {
// Handle the input with current handler
var result = this.currentHandler.handle(event.target.value);
// If you want you can use result to cahnge something in view
// this.setState({value: result});
}
render() {
return (
<div>
<button onClick={this.handleClick}>
Switch to B Component
</button>
<textarea onChange={this.handleChange}>Text Area used between A class and B class</textarea>
</div>
)
}
}
I also edit the codebox example. You can find it here.
This can be achieved using a ref. ABComponent passes a ref to BComponent to attach to the textarea. When the state of ABComponent updates to component = 'B', then the ref is used to set focus. Use a ref passed to AComponent to grab its textarea value before it's unmounted, then set the value of the textarea in B to it.
import React, { Component, createRef } from "react";
...
class AComponent extends Component {
render() {
const { textareaRef } = this.props;
return <textarea ref={textareaRef} defaultValue="A Component" />;
}
}
class BComponent extends Component {
render() {
const { textareaRef } = this.props;
return <textarea ref={textareaRef} defaultValue="Should be A Component text" />;
}
}
class ABComponent extends Component {
state = { component: "A" };
refA = createRef();
refB = createRef();
componentDidUpdate(prevProps, prevState) {
const { component, content } = this.state;
if (prevState.component !== component) {
if (component === "B") {
this.refB.current.focus();
this.refB.current.value = content;
}
}
}
render() {
return (
<div>
<button
onClick={e =>
this.setState({ component: "B", content: this.refA.current.value })
}
>
Switch to B Component
</button>
{this.state.component === "A" && <AComponent textareaRef={this.refA} />}
{this.state.component === "B" && <BComponent textareaRef={this.refB} />}
</div>
);
}
}

How to Set a state of parent component from child component in react js

how do i change the state of parent in child component
I'm trying to create a popover in react
Parent component
class App extends Component {
constructor(props) {
super(props);
this.state = {
status: false,
anchorEl: null
};
}
showpop = () => {
this.setState({ status: !this.state.status });
};
render() {
return (
<React.Fragment>
<p id="popup" onClick={this.showpop}>
Click me
</p>
{this.state.status ? (
<Popup status={this.state.status}>test</Popup>
) : (
""
)}
</React.Fragment>
);
}
}
i just passed the state of status to popover component .
This is the child component
export default class popup extends Component {
constructor(props) {
super(props);
this.state = {
popupStatus: false
};
}
componentWillMount() {
document.addEventListener("click", this.handleclick, false);
}
componentWillUnmount() {
document.removeEventListener("click", this.handleclick, false);
}
handleclick = e => {
if (this.node.contains(e.target)) {
return;
} else {
//here what to do?
}
};
render() {
return (
<React.Fragment>
<Mainbox
status={this.props.status}
ref={node => {
this.node = node;
}}
>
{this.props.children}
</Mainbox>
</React.Fragment>
);
}
}
In the handleclick function else part ,
i tried these
I change the node style display to none but in the window need two clicks to show a popover
you can see the Mainbox component in child is created using styed components library
is there any way to hide the elemet and change the parent state?
You can just pass a method reference to child:
<Popup status={this.state.status} showpop={this.showpop}>test</Popup>
handleclick = e => {
if (this.node.contains(e.target)) {
return;
} else {
this.props.showpop()
}

How to pass the changed state from child component to its parent in ReactJS

I am trying to understand how to pass a changed state from child component to its parent in ReactJS? so far the following code changes the child state but not the parents state, any clue what I am doing wrong?
I am using redux to get product array from mongodb.
Product array example:
[
{
“_id”: “2331”,
“department”: “Shoes”,
“category”: “Shoes/Women/Pumps”,
“name”: “Calvin Klein”,
“title”: “Evening Platform Pumps”,
“description”: “Perfect for a casual night out or a formal event.”,
“style”: “Designer”,
"colors": ["red","yellow","red","black"]
},
{
“_id”: “30671”,
“department”: “Shoes”,
“category”: “Shoes/Women/Pumps”,
“name”: “zara”,
“title”: “Evening Platform Pumps”,
“description”: “Perfect for a casual night out or a formal event.”,
“style”: “Designer”,
"colors": ["red","yellow","red","black"]
}
]
Parent Component
import React, { Component } from 'react'
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
};
}
componentDidMount() {
this.props.getProducts();
}
componentDidUpdate(prevProps, prevState) {
if (this.props.product.products !== prevState.products) {
this.setState({ products: this.props.product.products });
}
}
onUpdateProducts = (e) => {
const newProducts = this.state.products;
this.props.updateProductName(newProducts);
};
render() {
const { products } = this.state;
if (isEmpty(products)) {
productContent = (
<div>
<p className="lead text-muted">Error Empty Products </p>
</div>
);
} else {
const productArr = products.map((product) => (
<Child key={product._id} product={product} />
));
productContent = (
<div>
{productArr}
</div>
);
}
return (
<div className="container">
{productContent}
<div className="row">
<div className="col-md-12">
<button className="btn " onClick={this.onUpdateProducts}>
Submit
</button>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
product: state.product
});
export default connect(mapStateToProps, {
getProducts,updateProductName
})(Parent);
Child Component
import React, { Component } from 'react'
export default class Child extends Component {
constructor(props) {
super(props);
this.state = {
product: this.props.product,
};
}
componentDidUpdate(prevProps, prevState) {
if (this.props.product !== prevProps.product) {
this.setState({
product: this.props.product
});
}
}
onChangeProductName = (e) => {
const newProduct = Object.assign({}, this.state.product, {
name: e.target.value
});
this.setState({ product: newProduct }, function() {
console.log('onChangeProductName: ', this.state.product);
});
};
render() {
const { product } = this.state;
return (
<div>
<TextInput
placeholder="Product Name"
name="prd_name"
value={product.name}
onChange={this.onChangeProductName}
/>
</div>
)
}
}
There are two ways for a child component to update the parent component:
Without using Redux, you can pass a function as a prop of the child component, so the child component can call this function to update the parent component.
Store the data in the Redux store. The child component dispatches an action which updates the Redux state, where the parent component gets data.
A simple example would explain the concept of passing the changed state from child to the parent.
Component A:
export default class A extends Component{
//This is a callback
handleStateChange = (value) ={
console.log("value", value);//you get the value here when state changes in B(Child) component
}
render(){
return(
<div>
<B handleStateChange={this.handleStateChange} />
</div>
)
}
}
Component B:
export Class B extends Component{
constructor(props){
super(props);
this.state={
value: "01"
}
}
handleButton = () => {
const value = "02";
this.setState({
value: "02"
});
this.props.handleStateChange(value);
}
render(){
return(
<div>
<button onClick={this.handleButton} />
</div>
)
}
}
Or you can directly pass the state if you call this.props.handleStateChange(this.state.value); this in render directly on any event handler if you want to pass updated state
As #Ying zuo mentioned you need to use redux to get the changed state value of child component in parent component.
When state changes in child component, you make a redux action call by passing the value as param and set that in the state in reducer and get the state in your parent component
Hope that explains the concept.
You have to pass the child a function.
In the child component you are setting state to be equal to props value, and then you are updating state. This has no connection to parent class - you also shouldn't modify props just as an aside.
The solution is to pass a function from the parent to child. This function will update the parent state, and because you are passing the parent state to the child, it will also be updated.
So in your parent class you could do something like:
onChangeProductName = (value, i) => {
const new_product_array = [...this.state.products];
new_product_array[i].name = value;
this.setState({ products: new_product_array});
};
You would need to pass this to the child
const productArr = products.map((product, i) => (
<Child
key={product._id}
product={product} onChangeName={this.onChangeProductName.bind(this)}
index={i} />
));
And then call it in the child
<TextInput
placeholder="Product Name"
name="prd_name"
value={product.name}
onChange={() => this.props.onChangeName(product, this.props.index)}
/>
The child component then doesn't need all the state tracking.

Resources