Component communication - React JS - reactjs

How to make state change from outside of class or from prop.SomeFunction () ?
I use typescript in combination. Yes i already have the class :
CLASS :
import * as React from "react";
import { Label } from "../../components/label/label";
import { TextBox } from "../../components/textBox/textBox";
import IApp from "../../interfaces/global-interfaces";
import { myStyle } from "./style";
interface HeaderI {
background: string;
myFunc(): void;
// elements: any[];
// add(id: number, content: any, event: any): void;
}
interface HeaderStateI extends IApp.ElementI {
background: string;
elements: any[];
}
export class Header extends React.Component< HeaderI, HeaderStateI , any > {
public myRef: React.RefObject<Label>;
constructor(args: any) {
super(args);
this.state = { enabledComponent : true,
visibility: true,
debugView: false,
background: args.background,
elements: [],
};
this.myRef = React.createRef();
this.add = this.add.bind(this);
console.log("state elements:" , this.state.elements);
}
public test() {
alert("Y click on header element");
}
public printMe() {
console.log("Layout Header is active :");
}
// setText = (e: React.ChangeEvent) => {
// this.setState ( { enabledComponent : true , background : (e.target as HTMLInputElement).value } );
// }
public render() {
if ( this.state.debugView === false ) {
return (
<div style={myStyle} onClick={this.addN} >
<Label name="headerName" text="i am header paragraph!" />
{this.state.elements.map((i: any) => {
return <span key={i} >{i}</span>;
})}
</div>
);
} else {
this.printMe();
return (
<div style={myStyle} >
<Label ref={this.myRef} name="headerName" text="i am header paragraph!"/>
{this.state.elements.map((i: any) => {
return <li key={i} >{i}</li>;
})}
</div>
);
}
}
public componentDidUpdate(prevProps: any) {
// Typical usage (don't forget to compare props):
console.warn("prevProps name is: " + prevProps.name);
if (this.props.background !== prevProps.background) {
this.printMe();
} else {
console.log("Background is same no update.");
}
}
public add = (id: number, content: any, event: any ) => {
this.setState(
{
elements: [React.createElement("div", { key: 2 , onclick : null }, "tyest 12")],
visibility : false,
},
);
// tslint:disable-next-line:no-unused-expression
console.log(" add from class in state elements, visible is " , this.state.visibility );
}
public addN(event: MouseEvent | Event) {
this.setState(
{
elements: [React.createElement("div", { key: 2 , onclick : null }, "tyest 12")],
visibility : false,
},
);
// tslint:disable-next-line:no-unused-expression
console.log(" add from class in state elements, visible is NNNN " , this.state.visibility );
}
}
MAIN FILE :
const AddNewElement = Services.addNewElement;
const collectMe = <Header background="#127654"/>;
ReactDOM.render(
<>
{collectMe}
<h3>Page title - inline text only example </h3>
<Title name="title" text="Hello react title" />
<Label name="root" text="I am text label component!"/> <br/>
<TextBox name="root" text="I am textBox component!"/>
</>,
document.getElementById("root"),
);
// This space is already out of class so why i can't change it ???
collectMe.props.background = "#121212";
console.log(collectMe.props);
There is lot of problem with react js.
In this post there's methods but still not working for me :
https://www.javascriptstuff.com/component-communication/#2-instance-methods
But examples are too pure with out args passing etc...
Is it react just a commercial puffed-up project.

props are read-only.
Whether you declare a component as a function or a class, it must never modify its own props.
What you need to do here is to use state. And to use state, you need to have class based component. Then, you can update the parent component state from child component using a callback function.
Take a look at this post to update the parent state from child component.

Related

Nested Function Returns Unexpected Result

I have a menu component in my header component. The header component passes a function to the menu component => default menu component. It's working but the function returns unwanted data.
the path my function is traveling through is:
homepage => header => menu => defaultMenu
The function is:
changeBodyHandler = (newBody) => {
console.log(newBody)
this.setState({
body: newBody
})
}
I pass the function from homepage => header like this:
<HeaderDiv headerMenuClick={() => this.changeBodyHandler}/>
then through header => menu => defaultMenu using:
<Menu MenuClick={this.props.headerMenuClick} />
//==================== COMPONENT CODES ==========================//
homepage:
class Homepage extends Component {
constructor(props){
super(props);
this.state = {
body: "Homepage"
}
this.changeBodyHandler = this.changeBodyHandler.bind(this)
}
changeBodyHandler = (newBody) => {
console.log(newBody)
this.setState({
body: newBody
})
}
render() {
return (
<div>
<HeaderDiv headerMenuClick={() => this.changeBodyHandler}/>
{ this.state.body === "Homepage" ?
<HomepageBody />
: (<div> </div>)}
</div>
);
}
}
header:
class HeaderDiv extends Component {
constructor(props) {
super(props);
this.state = {
showMenu: 'Default',
}
}
render(){
return (
<Menu MenuClick={this.props.headerMenuClick}/>
);
}
}
menu:
import React, { Component } from 'react';
import DefaultMenu from './SubCompMenu/DefaultMenu';
import LoginCom from './SubCompMenu/LoginCom';
import SingupCom from './SubCompMenu/SingupCom';
class Menu extends Component {
//==================================================================
constructor(props){
super(props);
this.state = {
show: this.props.shows
};
this.getBackCancelLoginForm = this.getBackCancelLoginForm.bind(this);
}
//===============================================================
//getBackCancelLoginForm use to hindle click event singin & singup childs
//===============================================================
getBackCancelLoginForm(e){
console.log("Hi")
this.setState({
show : "Default"
})
}
//=================================================================
// getDerivedStateFromProps changes state show when props.shows changes
//===============================================================
componentWillReceiveProps(nextProps) {
if(this.props.show != this.nextProps){
this.setState({ show: nextProps.shows });
}
}
//======================================================================
render() {
return (
<div>
{ this.state.show === "Singin" ?
<LoginCom
cencelLogin={this.getBackCancelLoginForm.bind(this)}
/>
: (<div> </div>)}
{ this.state.show === "Singup" ?
<SingupCom
cencelLogin={this.getBackCancelLoginForm.bind(this)}
/>
: (<div> </div>)}
{ this.state.show === "Default" ?
<DefaultMenu MenuClicks={this.props.MenuClick}/> : (<div> </div>)}
</div>
);
}
}
Default menu:
class DefaultMenu extends Component {
render() {
return (
<div className="box11" onClick={this.props.MenuClicks("Homepage")}>
<h3 className="boxh3" onClick={this.props.MenuClicks("Homepage")}>HOME</h3>
);
}
}
//================ Describe expected and actual results. ================//
I'm expecting the string "Homepage" to be assigned to my state "body"
but console.log shows:
Class {dispatchConfig: {…}, _targetInst: FiberNode, nativeEvent: MouseEvent, type: "click", target: div.box11, …}
instead of "Homepage"
Use arrow functions in onClick listener, in above question Change DefaultMenu as:
class DefualtMenu extends Component {
render() {
return (
<div className="box11" onClick={() => this.props.MenuClicks("Homepage")}>
<h3 className="boxh3">HOME</h3>
</div>
);
} }
For arrow functions learn from mozilla Arrow Functions
I hope this helps.

Stack with style in react js

What is a point in react. We have props who's readOnly and i cant edit it and we have state also can't edit from out of class space ?!
I follow tutorials ...
if i setup {this.state.myStyle} , myStale become's readonly ?!
Here is whole class :
import * as React from "react";
import { CSSProperties } from "react";
import * as ReactDOM from "react-dom";
import { Label } from "../../components/label/label";
import IApp from "../../interfaces/global-interfaces";
import Services from "../../services/services";
import { HeaderI, HeaderStateI } from "./header-interface";
// import { myStyle } from "./style";
enum myEventList {
iNeedSomeUpdate = "i-need-some-update",
}
export class Header extends React.Component< HeaderI, HeaderStateI , any > {
public myEvent = Services.CreateEvent(myEventList.iNeedSomeUpdate, {self: this} );
public myRef: React.RefObject<HTMLDivElement>;
public myDOM: Element | Text;
private myStyle: IApp.MyMinimumCssInterface = {
display: "block",
background: "#559d96",
height: "100px",
textAlign: "center",
};
constructor(args: any) {
super(args);
this.state = { enabledComponent : true,
visibility: true,
debugView: false,
background: args.background,
elements: [],
// tslint:disable-next-line:object-literal-shorthand
myStyle: this.myStyle,
};
// e.detail.data.self..background = this.state.background;
this.myRef = React.createRef();
this.add = this.add.bind(this);
}
// Override func
public componentDidMount() {
this.myDOM = this.myRef.current;
this.myDOM.addEventListener(myEventList.iNeedSomeUpdate, this.updateOnMyEvent);
}
public updateOnMyEvent(e: CustomEvent) {
e.detail.data.self.printMe();
console.log("My custom event is done!");
e.detail.data.self.adapt();
}
public printMe() {
console.log("Layout Header is active and update is on");
}
public render() {
if ( this.state.debugView === false ) {
return (
<div ref={this.myRef} style={this.state.myStyle} onClick={this.TestEvent.bind(this)} >
<Label name="headerName" text="i am header paragraph!" />
{this.state.elements.map((i: any) => {
return <span key={i} >{i}</span>;
})}
</div>
);
} else {
this.printMe();
return (
<div style={this.state.myStyle} ref={this.myRef} >
<Label name="headerName" text="i am header paragraph!"/>
{this.state.elements.map((i: any) => {
return <li key={i} >{i}</li>;
})}
</div>
);
}
}
public componentDidUpdate(prevProps: any) {
// Typical usage (don't forget to compare props):
console.warn("prevProps name is: " + prevProps.name);
if (this.props.background !== prevProps.background) {
this.printMe();
} else {
console.log("Background is same no update.");
}
}
public add = (id: number, content: any, event: any ) => {
let localArr: any[] = [];
localArr = this.state.elements;
localArr.push(React.createElement("div", { key: id , onClick : null }, content));
this.setState(
{
elements: localArr,
visibility : false,
},
);
// tslint:disable-next-line:no-unused-expression
console.log(" add from class in state elements, visible is " , this.state.visibility );
}
public TestEvent(event: MouseEvent) {
this.add( 1 , "fffff", null);
this.add( 2 , "zzzzzz", null);
this.myDOM.dispatchEvent(this.myEvent);
}
public adapt() {
this.myStyle.background = "lime";
this.setState({
myStyle: this.myStyle,
});
}
}
Because myStyle is 'frozen', you need to clone the object, make changes and then write it back using setState.
In ES6 you can use a pattern like this:
public adapt() {
const {myStyle} = this.state
let newMyStyle = {...myStyle}
newMyStyle.background = "lime";
this.setState({
myStyle: newMyStyle,
});
}
There are several ways how to manage this i.e.
const myStyle = Object.assign({}, this.state.myStyle, { background: "lime" })
this.setState({ myStyle })

Nested components losing focus on first onchange

I've got 3 components as follows and the first on change on the input field makes it lose focus. I've tried giving the input fields and divs keys but it didn't solve my problem. I'm new to React so I might be doing something basic incorrectly. I'm not creating a new function inside a render method which is the reason for most losing focus issues documented in stackoverflow. Hence the reason for the new post.
Index.tsx
import * as React from "react";
import { IQuery, Queries } from "./components/Queries/Queries";
interface IManageResponseState {
primaryQuery: IQuery;
alternateQueries: IQuery[];
}
export class ManageResponse extends React.Component<any, IManageResponseState> {
constructor(props: any) {
super(props);
this.state = {
primaryQuery: {
queryText: "This is my primary query text",
id: 0
}, alternateQueries: [{
queryText: "this is my alternate query text 1",
id: 1
},
{
queryText: "this is my alternate query text 2",
id: 2
}]
};
this.addFunc = this.addFunc.bind(this);
this.removeFunc = this.removeFunc.bind(this);
this.primaryChangedFunc = this.primaryChangedFunc.bind(this);
this.alternateChangedFunc = this.alternateChangedFunc.bind(this);
}
public addFunc(text: string) {
const newQueries = this.state.alternateQueries.filter(q => true);
newQueries.push({ id: 0, queryText: text });
this.setState({ alternateQueries: newQueries });
};
public removeFunc(index: number) {
this.setState({ alternateQueries: this.state.alternateQueries.splice(index, 1) });
console.log("Remove called:" + index);
};
public primaryChangedFunc(text: string) {
const query = {
queryText: text,
id: 0
};
this.setState({ primaryQuery: query });
console.log("changed primary called:" + text);
}
public alternateChangedFunc(index: number, text: string) {
const item = this.state.alternateQueries[index];
const newQueries = this.state.alternateQueries.filter(q => true);
newQueries[index] = {
queryText: text,
id: item.id
};
this.setState({ alternateQueries: newQueries })
console.log("changed alternate called:" + text);
}
public render() {
return (
<React.Fragment>
<Queries primaryQuery={this.state.primaryQuery} alternateQueries={this.state.alternateQueries} onAddQuery={this.addFunc} onRemoveQuery={this.removeFunc} onPrimaryChanged={this.primaryChangedFunc} onAlternateQueryChanged={this.alternateChangedFunc} />
</React.Fragment>
);
}
}
Queries.tsx
import * as classnames from 'classnames';
import * as React from "react";
import { AlternateQueryItem } from "../AlternateQueryItem/AlternateQueryItem";
import './Queries.scss'
export interface IQuery {
queryText: string;
id: number;
}
export interface IQueryProps {
primaryQuery: IQuery;
alternateQueries: IQuery[];
onPrimaryChanged: (queryText: string) => void;
onAlternateQueryChanged: (index: number, queryText: string) => void;
onAddQuery: (queryText: string) => void;
onRemoveQuery: (atIndex: number) => void;
}
interface IQueryState {
primaryQueryInvalid: boolean;
}
class Queries extends React.Component<IQueryProps, IQueryState> {
constructor(props: IQueryProps) {
super(props);
this.onPrimaryQueryChanged = this.onPrimaryQueryChanged.bind(this);
this.state = { primaryQueryInvalid: false };
}
public render() {
return (
<main className="query-main-container">
<div className="primary-query-container">
<div className="form-group">
<h5 className={classnames("primary-query-header", { 'invalid': this.state.primaryQueryInvalid })}>Primary query</h5>
<div className="primary-query">
<div>{ "hello " + this.state.primaryQueryInvalid }</div>
<input type="text" value={this.props.primaryQuery.queryText} onChange={this.onPrimaryQueryChanged} className="form-control"/>
</div>
</div>
</div>
<div>
<h5 className="query-header">Query variations</h5>
<hr />
</div>
<div className="query-container">
{this.props.alternateQueries && this.props.alternateQueries.map((q, i) => this.renderQuery(q, i))}
</div>
</main>
);
}
public renderQuery(query: IQuery, key: number) {
return (
<AlternateQueryItem onChanged={this.props.onAlternateQueryChanged} query={query} key={key} index={key} />
);
}
private onPrimaryQueryChanged = (ev: React.FormEvent<HTMLInputElement>) => {
this.setState({ primaryQueryInvalid: ev.currentTarget.value === ""})
this.props.onPrimaryChanged(ev.currentTarget.value)
}
}
export { Queries };
AlternateQueryItem.tsx
import * as React from "react";
import { IQuery } from "../Queries/Queries";
export interface IAlternateQueryProps {
query: IQuery;
onChanged: (index: number, queryText: string) => void;
index: number;
}
class AlternateQueryItem extends React.Component<IAlternateQueryProps> {
constructor(props: IAlternateQueryProps) {
super(props);
this.onItemChanged = this.onItemChanged.bind(this);
}
public render() {
return (
<div className="form-group">
<div className="alternate-query">
<input type="text" value={this.props.query.queryText} onChange={this.onItemChanged} className="form-control" />
</div>
</div>
);
}
public onItemChanged = (ev: any) => {
this.props.onChanged(this.props.index, ev.currentTarget.value)
}
}
export { AlternateQueryItem };

TypeScript React setState is not working

I have a component with input like this
import * as React from 'react'
import { InputNumber, Button } from 'antd'
interface IProps {
value: number
}
interface IState {
value: number
}
class ConfirmInput extends React.Component<IProps, IState> {
public readonly state = {
value: 0
}
static getDerivedStateFromProps = ({ value }: IProps) => ({ value })
public render () {
return (
<div>
<InputNumber
value={this.state.value}
formatter={value => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={(value): any => value && value.replace(/\$\s?|(,*)/g, '')}
onChange={(value: number) => {
console.log('value', value)
this.setState({ value })
}}
/>
<Button
onClick={() => console.log('this.state.value', this.state.value)}
type="primary"
>
Bid
</Button>
</div>
)
}
}
export default ConfirmInput
on the InputNumber I have on change and that gets a number as a value, however this.setState doesn't change the value.
There are no compiler errors and everythings seems to be ok with types.
What am I missing in here?
You'll need to store the original value in the state so that your getDerivedStateFromProps implementation can overwrite the current value only when the prop changes. This should hopefully work (not tested):
interface IState {
value: number
origValue: number
}
class ConfirmInput extends React.Component<IProps, IState> {
// ...
static getDerivedStateFromProps = (props: IProps, state: IState) =>
(props.value != state.origValue ? { origValue: props.value, value: props.value } : {})
// ...
}

How to handle input change for semantic-ui-react Dropdown object in a form without creating multiple handlers?

the semantic-ui-react Dropdown object does not accept a name or id attribute, and therefore the change cannot be handled in the same way as other form elements. the docs show this:
import React, { Component } from 'react'
import { Dropdown, Grid, Segment } from 'semantic-ui-react'
const options = [
{ key: 1, text: 'One', value: 1 },
{ key: 2, text: 'Two', value: 2 },
{ key: 3, text: 'Three', value: 3 },
]
export default class DropdownExampleControlled extends Component {
state = {}
handleChange = (e, { value }) => this.setState({ value })
render() {
const { value } = this.state
return (
<Grid columns={2}>
<Grid.Column>
<Dropdown
onChange={this.handleChange}
options={options}
placeholder='Choose an option'
selection
value={value}
/>
</Grid.Column>
<Grid.Column>
<Segment secondary>
<pre>Current value: {value}</pre>
</Segment>
</Grid.Column>
</Grid>
)
}
}
when combining inputs into a single event handler, there's no tidy way to pull out an identifier to update the state for the dropdown. how is this normally handled?
thanks
One option is to use a simple wrapper(not unnecessary bloated) over different input controls, so that even if we change a control library we will have limited change scope. Below is simple example of such wrapper, and shows a simple approach to use same value change handler for multiple fields (even for different type of input controls):
import React, { Component } from 'react';
import { render } from 'react-dom';
const FIELD_NAMES = {
FirstName: 'FirstName',
LastName: 'LastName',
};
const TEXT_CONTAINER_STYLE = { padding: 5 };
function MyTextInput(props) {
const {
name,
onChange,
value,
} = props;
function handleValueChange(e) {
onChange(name, e.target.value);
}
return (
<div style={TEXT_CONTAINER_STYLE}>
<input onChange={handleValueChange} value={props.value} />
</div>
);
}
class App extends Component {
constructor() {
super();
this.state = {
};
this.state[FIELD_NAMES.FirstName] = '';
this.state[FIELD_NAMES.LastName] = '';
}
handleValueChange = (fieldName, fieldValue) => {
if (fieldName) {
let newState = {};
switch (fieldName) {
case FIELD_NAMES.FirstName:
newState[FIELD_NAMES.FirstName] = fieldValue;
break;
case FIELD_NAMES.LastName:
newState[FIELD_NAMES.LastName] = fieldValue;
break;
}
this.setState(newState);
}
}
getFieldValue = (fieldName) => {
return this.state[fieldName]
}
render() {
return (
<div>
<MyTextInput
name={FIELD_NAMES.FirstName}
value={this.getFieldValue(FIELD_NAMES.FirstName)}
onChange={this.handleValueChange}
/>
<MyTextInput
name={FIELD_NAMES.LastName}
value={this.getFieldValue(FIELD_NAMES.LastName)}
onChange={this.handleValueChange}
/>
<div>
{`First Name : ${this.getFieldValue(FIELD_NAMES.FirstName)}`}
</div>
<div>
{`Last Name : ${this.getFieldValue(FIELD_NAMES.LastName)}`}
</div>
</div >
);
}
}
render(<App />, document.getElementById('root'));
Working example

Resources