Reuse same DOM element in two different react components - reactjs

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>
);
}
}

Related

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>
)
}
}

Rerender Child without rendering parent component React js

I am learning React js. I need to rerender one of the child components from the parent component. One way is I can use setState for the matrix but the entire matrix which is parent component will be rerendered instead I want to rerender only one child component. This have added by code below.
Child.js
import React from 'react';
class Child extends React.Component {
constructor(props){
super(props);
this.state = {
text : ""
};
}
updateParent(text) {
if(text) {
this.setState({text : text});
}
}
render() {
return(
<div>Child {this.state.text}</div>
);
}
}
export default Child;
Parent.js
import React from 'react';
import Child from './Child'
class Parent extends React.Component {
constructor(props){
super(props);
this.state = {
table : [[<Child key={11}/>, <Child key={12}/>, <Child key={13}/>],
[<Child key={21}/>, <Child key={22}/>, <Child key={23}/>]],
i : 0,
j : 0
};
}
componentDidMount() {
this.timerID1 = setInterval(() => this.updateTable(), 1000);
}
updateTable() {
//this.state.table[this.state.i][this.state.j].updateParent("");
this.state.j++;
if( this.state.j % 3 == 0) {
this.state.i++;
this.state.i %= 2;
}
//this.state.table[this.state.i][this.state.j].updateParent("*");
// or tempTable[i][j] = <Child key={ij} text={"*"}/>; this.setState({table: tempTable});
this.state.j++;
}
createTable() {
let table = []
for(let i = 0; i < 2; i++) {
table.push( <div key={i} style={{display:"flex"}}>{this.state.table[0]}</div> )
}
return table;
}
render() {
return(
<div>{this.createTable()}</div>
);
}
}
export default Parent;
Don't store Child component instances in state, instead render them dynamically
You can implement Child as a PureComponent so that if no props or state change for it, it doesn't re-render
Do not mutate state directly like you do this.state.j++ and so on. Use setState
Parent.js
export default class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
table: this.createTableData(3),
i: 0,
j: 0
};
}
createTableData(size) {
const arr = new Array(size);
for (var i = 0; i < size; i++) {
arr[i] = new Array(size).fill("");
}
return arr;
}
componentDidMount() {
this.timerID1 = setInterval(() => this.updateTable(), 1000);
}
componentWillUnmount() {
clearInterval(this.timerID1);
}
updateTable() {
let { i, j, table } = this.state;
j++;
if (j % 3 == 0) {
i++;
i %= 2;
}
const newTable = table.map((tr, row) => {
return tr.map((td, col) => {
if (row == i && col == j) {
return "*";
} else {
return "";
}
});
});
j++;
this.setState({
table: newTable,
i,
j
});
}
createTable() {
return this.state.table.map((row, i) => {
return (
<div className="row">
{row.map((col, j) => {
return <Child key={`${i + 1}${j + 1}`} text={col} />;
})}
</div>
);
});
}
render() {
return <div>{this.createTable()}</div>;
}
}
Child.js
class Child extends React.PureComponent {
render() {
console.log("child rerender", this.props.text);
return <div>Child {this.props.text} </div>;
}
}
working demo
NOTE: The demo only contains the display and performance optimization logic along with the architecture, The logic to update the indexes i, j needs to be done by you in updateTable method.
If you look at the demo, only the cell you whose value changed from "" to "*" and vice versa will re-render, the rest will not
you can rerender child component without rendering parent component
by using ref to call child function from parent and update child component
Parent.js
import React from "react";
import Table from "./Table";
import Child from "./Child";
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.table = [
[<Child key={11} />, <Child key={12} />, <Child key={13} />],
[<Child key={21} />, <Child key={22} />, <Child key={23} />],
];
this.i = 0;
this.j = 0;
}
componentDidMount() {
this.timerID1 = setInterval(() => this.updateTable(), 1000);
}
updateTable() {
this.j++;
if (this.j % 3 == 0) {
this.i++;
this.i %= 2;
}
this.j++;
this.table[0].push(
<Child key={21} />,
<Child key={22} />,
<Child key={23} />
);
this.refs.table.updateTable(this.table[0]);
}
componentDidUpdate() {
console.log("component rerender");
}
render() {
return (
<div>
<h1>Parent Component</h1>
<Table ref="table" />
</div>
);
}
}
export default Parent;
Child.js
import React from 'react';
class Child extends React.Component {
constructor(props){
super(props);
this.state = {
text : ""
};
}
updateParent(text) {
if(text) {
this.setState({text : text});
}
}
render() {
return(
<div>Child {this.state.text}</div>
);
}
}
export default Child;
Table.js
import React, { Component } from "react";
class table extends Component {
state = {
table: [],
};
updateTable = (table) => {
this.setState({ table });
};
render() {
const { table } = this.state;
return (
<div>
{table.map((tableItem, i) => {
return <div key={i} style={{ display: "flex" }}>{tableItem}</div>;
})}
</div>
);
}
}
export default table;
componentDidUpdate will give log if rerendering is happen
Note: I did not use state in parent component. if you want to use parent component state then you have to stop rerendering by using shouldComponentUpdate lifecycle

how to pass state to component

I created MExample component in that component i have created this
export default class MExample extends Component {
_validate() {
if (validateDate(this.state.choseDate).status) {
if (validateList(this.state.list).status) {
var list = this.state.list;
var choseDate = this.state.choseDate;
console.log(list+choseDate)
this.setState({ visibleModal: null , list:[], choseDate:''})
} else {
alert("select list date")
}
} else {
alert("select monthly date ")
}
}
render() {
return (
// jsx
)}
export default class Mnavigate extends Component {
render() {
return (
<MExample list={this.state.list} choseDate = {this.state.choseDate}/>
// can i access value like this ?
)
}
How to use this.state.list and this.state.choseDate in other component in which i'm importing this component <MExample here i want list and choseDate value />
<MExample list={this.state.list} choseDate={this.state.choseDate} />
and inside MExample component
access through
this.props.list and this.props.choseDate
class MExample extends React.Component{
render(){
console.log(this.props.list);
return null;
}
}
You can create properties and pass them as props.
Create a component as below
import React, { Component } from 'react'
class MExample extends Component {
// You can access them via this.props
validate = () => {
console.log(this.prop.list);
console.log(this.prop.choseDate);
}
render() {
let {list,choseDate} = this.props;
// your code comes here
return (
<div>
</div>
)
}
}
export default MExample;
Pass the state in the properties.
<MExample list={this.state.list} choseDate={this.state.choseDate} />

Converting functional component to class component

I have one functional component, but as I need to use now state and more complex logic, I would like to convert it to class component.
But I don't know exactly how to get it working:
My functional component:
import React from 'react';
const FileList = (props) => {
const items = props.items.map((item) => {
return <p key={item.reqId} > { item.name }</ p>
});
return <div>{items}</div>
}
And I tried to do that:
export default class FileL extends React.Component {
constructor(props) {
super(props)
}
render() {
const { items } = this.props;
items = props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
});
return (
<div>{items}</div>
);
}
}
But this is not working.It says "items" is read-only.
I would like to keep the same functionality.
Any ideas?
In your render function
render() {
const { items } = this.props;
items = props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
});
return (
<div>{items}</div>
);
}
items is const so you can't override it. This has nothing to do with React. And you shouldn't reassign a props element, even if its defined with let. You might use the following:
render() {
const { items } = this.props;
return (
<div>
{
items.map((item) => <p key={item.reqId} > {item.name}</ p>)
}
</div>
);
}
You can try this,
export default class FileL extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{
this.props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
})
}
</div>
);
}
}
Actually you don't need to convert your component to class based component, as React 16.8 comes with Hooks. Using Hooks you can do whatever you can do with class based component. They let you use state and other React features without writing a class.

Call child component function from parent

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.
Path: Parent Component
export default class StudentPage extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
newStudentUserCreated() {
console.log('newStudentUserCreated1');
this.refs.studentTable.handleTableUpdate();
}
render() {
return (
<div>
<StudentTable
studentUserProfiles={this.props.studentUserProfiles}
ref={this.studentTable}
/>
</div>
);
}
}
Path: StudentTable
export default class StudentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
studentUserProfiles: props.studentUserProfiles,
};
this.handleTableUpdate = this.handleTableUpdate.bind(this);
}
handleTableUpdate = () => (event) => {
// Do stuff
}
render() {
return (
<div>
// stuff
</div>
);
}
}
UPDATE
Path StudentContainer
export default StudentContainer = withTracker(() => {
const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;
return {
studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
};
})(StudentPage);
My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.
I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.
One solution is to use refs.
In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
Demo (open console to view result): https://codesandbox.io/s/9102103xjo
class Parent extends React.Component {
constructor(props) {
super(props);
this.childTwo = React.createRef();
}
newUserCreated = () => {
this.childTwo.current.updateTable();
};
render() {
return (
<div className="App">
<ChildOne newUserCreated={this.newUserCreated} />
<ChildTwo ref={this.childTwo} />
</div>
);
}
}
class ChildOne extends React.Component {
handleSubmit = () => {
this.props.newUserCreated();
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
class ChildTwo extends React.Component {
updateTable() {
console.log("Update Table");
}
render() {
return <div />;
}
}

Resources