React: Page not updated - reactjs

module of MarketEvent.tsx,The function is a centralized controller:
import * as React from 'react';
import EventList from './EventList';
import FullReduce from './FullReduce';
import './MarketEvent.less'
export default class MarketEvent extends React.Component<{},any> {
public id: string;
public name: string;
public target: JSX.Element;
public defaultId: string;
public state = {
target: this.target,
defaultId: 'marketMain'
};
public constructor(defaultId:any) {
super(defaultId);
this.changeTarget = this.changeTarget.bind(this);
this.target = this.state.target;
console.log('传到父组件的ID:',this.state.defaultId);
switch (this.state.defaultId) {
case 'marketMain':
this.target = <EventList currentRoute={this.changeTarget}/>;
break;
case 'fullReduce':
this.target = <FullReduce currentRoute={this.changeTarget}/>;
break;
default:
this.target = <EventList currentRoute={this.changeTarget}/>;
}
}
public componentWillMount(){
console.log('componentWillMount MarketEvent');
}
public componentDidMount(){
console.log('componentDidMount MarketEvent');
}
public changeTarget = (id: string) => {
console.log('子组件传到父组件的ID:',this.state);
this.setState({
defaultId: id
})
};
public render(){
return(
<div>
{this.target}
</div>
)
}
}
module of EventList.tsx,The function is to show 3 lists.:
import * as React from 'react';
import './MarketEvent.less'
interface EventListProps {
currentRoute: any
}
export default class EventList extends React.Component<EventListProps,any> {
public componentWillMount(){
console.log('componentWillMount EventList')
}
public componentDidMount(){
console.log('componentDidMount EventList')
}
public refName = (id: string) => {
this.props.currentRoute(id);
};
public render(){
return(
<div className="market_event">
<div className="market_top">
营销活动
</div>
<div className="market_body">
<ul className="market_ul">
<li onClick={this.refName.bind(this,'fullReduce')}><a href="javascript:;"><span className="l1">减</span>
<div className="event_box">
<h2>店铺满减</h2>
<i>促销</i><i>客单价</i>
<p>下单满足条件可享受减免</p>
</div>
</a></li>
<li><a href="javascript:;"><span className="l2">店</span>
<div className="event_box">
<h2>店铺代金券</h2>
<i>拉新</i><i>引流</i>
<p>进店时可领取店铺专用代金券</p>
</div>
</a></li>
<li><a href="javascript:;"><span className="l3">促</span>
<div className="event_box">
<h2>折扣促销</h2>
<i>新品</i><i>爆款</i>
<p>下单满足条件可享受减免</p>
</div>
</a></li>
</ul>
</div>
</div>
)
}
}
module of FullReduce.tsx,Act as a detail page in a list:
import * as React from 'react';
import {Button} from "antd";
interface FullReduceProps {
currentRoute: any
}
export default class FullReduce extends React.Component<FullReduceProps,any> {
public componentWillMount(){
console.log('componentWillMount FullReduce');
}
public componentDidMount(){
console.log('componentDidMount FullReduce')
}
public refName = (id:string) => {
this.props.currentRoute(id);
};
public render(){
return(
<div>
<Button htmlType='button' onClick={this.refName.bind(this,'marketMain')}>返回</Button>
已经进入了店铺满减页面了
</div>
)
}
}
The effect I want to achieve is click on a list in EventList. tsx, return an ID to the centralized controller MarketEvent. tsx, and then render the corresponding page by judgment,But after clicking, I found that defaultID had changed, and the page was not rendered.I print this.state on the console and find that the target in this.state is undefined.
I don't know why. Is there a good hand to help me? Thank you very much!!

The MarketEvent constructor does not run again after the state is changed. If you want the switch statement to run again to choose a different subcomponent to show, move it to the render method:
export default class MarketEvent extends React.Component<{},any> {
public id: string;
public name: string;
public defaultId: string;
public state = {
defaultId: 'marketMain'
};
public constructor(defaultId:any) {
super(defaultId);
this.changeTarget = this.changeTarget.bind(this);
console.log('传到父组件的ID:',this.state.defaultId);
}
public componentWillMount(){
console.log('componentWillMount MarketEvent');
}
public componentDidMount(){
console.log('componentDidMount MarketEvent');
}
public changeTarget = (id: string) => {
console.log('子组件传到父组件的ID:',this.state);
this.setState({
defaultId: id
})
};
public render(){
let target;
switch (this.state.defaultId) {
case 'marketMain':
target = <EventList currentRoute={this.changeTarget}/>;
break;
case 'fullReduce':
target = <FullReduce currentRoute={this.changeTarget}/>;
break;
default:
target = <EventList currentRoute={this.changeTarget}/>;
}
return(
<div>
{target}
</div>
)
}
}

Related

React Component Not Rerendering - Base Class

I'm having an issue where a component isn't rerendering. There's a base class that's needed to be overriden in order to perform testing.
I'm expecting it to fire after the handleChange function is called because someone typed in the textbox.
BASE CLASS
import * as React from 'react';
import { ISiteListRendererProps } from "./SiteListRendererProps";
import { SitelistState, SiteItem } from './ISitelistState';
export default class SiteListRendererBase extends React.Component<ISiteListRendererProps, SitelistState> {
public SiteItemsPremise : SiteItem[];
public SiteItemsOnline : SiteItem[];
public SearchText: string;
public constructor(props) {
super(props);
}
public render(): React.ReactElement<ISiteListRendererProps> {
return (<div id="SiteListRendererNull"></div>);
}
}
CHILD CLASS
import * as React from 'react';
import { ISiteListRendererProps } from "./SiteListRendererProps";
import SiteListRendererBase from './SiteListRendererBase';
export default class SiteListRenderer extends SiteListRendererBase {
public constructor(props) {
super(props);
this.state = {SearchText: "", SiteItemsPremise: null, SiteItemsOnline: null, OnPremiseUrl: null};
//this.handleChange = this.handleChange.bind(this);
}
public render(): React.ReactElement<ISiteListRendererProps> {
console.log("rerender: " + this.state.SearchText);
console.log(this.SearchText);
let siteItems = this.SiteItemsPremise.concat(this.SiteItemsOnline);
siteItems.sort((a, b) => (a.Title > b.Title ? 1 : -1));
return (
<div>
<div>
<input type="text" onChange={this.handleChange} />
</div>
{siteItems
.filter(
(item, index) =>
this.state.SearchText.length == 0 ||
item.Title.toUpperCase().indexOf(
this.state.SearchText.toUpperCase()
) >= 0
)
.map((item, index) => (
<p key={item.Url}>
{item.Title + " (" + item.Location + ")"}{this.SearchText}
</p>
))}
</div>
);
}
handleChange = (e) => {
this.setState({ SearchText: e.target.value});
this.forceUpdate();
}
}
PROPS AND STATE FOR COMPLETENESS
export class SitelistState {
public SiteItemsOnline: SiteItem[];
public SiteItemsPremise: SiteItem[];
public SearchText: string;
public OnPremiseUrl: string;
}
export class SiteItem {
public Title: string;
public Url: string;
public Location: string;
public WebTemplate: string;
}
export interface ISiteListRendererProps {
SiteItemsPremise : SiteItem[];
SiteItemsOnline : SiteItem[];
SearchText: string;
}

Property 'isMounted' does not exist on type 'ErrorBoundary'

I'm practicing typescript and I've run into this bug that won't let me move forward: Property 'isMounted' does not exist on type 'ErrorBoundary'.
I have the following Boundary component
// React
import React from 'react'
// Librarys
import ReportIcon from '#mui/icons-material/Report';
type ChildrenProp = {
children: React.ReactNode,
}
type BoundaryState = {
error: null|string
}
export default class ErrorBoundary extends React.PureComponent<ChildrenProp, BoundaryState> {
constructor(props:ChildrenProp) {
super(props)
this.isMounted = false
this.state = {
error: null,
}
}
componentDidMount() {
this.isMounted = true
}
componentDidCatch(error) {
this.isMounted && this.setState({ error: error.message })
}
componentWillUnmount() {
this.isMounted = false
}
render() {
if (this.state.error) {
return (
<div className="d-flex h-100 align-items-center justify-content-center flex-column text-muted text-center">
<ReportIcon className="mb-1 fs-4" />
<h4 className="text-secondary">Application Error:</h4>
<code className="col-10 mx-auto">{JSON.stringify(this.state.error)}</code>
</div>
)
}
return this.props.children
}
}
I need to understand why Typescript throws me this error, the this.isMounted is the cause of this error, but how can I assign a type?. Thanks a lot
Add public isMounted: boolean; to the component to declare it (the public is optional)
export default class ErrorBoundary extends React.PureComponent<ChildrenProp, BoundaryState> {
public isMounted: boolean;
constructor(props:ChildrenProp) {
...
}

TypeError: Cannot read property 'vote' of null in render view

In this component I get a Promise object in the properties, I try to put it in state, but when the view is rendered, I get the message "TypeError: Cannot read property 'vote' of null", asking for a solution to my problem, I spent two hours on it and I don't see the end. What should I do differently?
import { IVoteDetailsProps } from "./IVoteDetailsProps";
import { IVoteDetailsState } from "./IVoteDetailsState";
export class VoteDetails extends React.Component<IVoteDetailsProps, IVoteDetailsState>{
constructor(props: IVoteDetailsProps) {
super();
console.log(props)
}
componentDidMount() {
let data = this.props.voteDetails;
data.then(result => this.setState({
vote: result
}));
};
public render(): React.ReactElement<IVoteDetailsState> {
return (
<table >
<tbody>
{this.state.vote && this.state.vote.map(el => {
<tr id={el.id.toString()}>
<td>{el.title}</td>
<td>{el.voteType}</td>
</tr>
})}
</tbody>
</table>
)
}
}
export interface IVoteDetailsProps {
voteDetails: Promise<IVoteDetailsData[]>;
}
export interface IVoteDetailsData{
id: number;
title: string;
voteType: string;
}
import React = require("react");
import { VoteDetails } from "../VoteDetails/VoteDetails";
import { IVoteListProps } from "./IVoteListProps";
export class VoteList extends React.Component<IVoteListProps, {}> {
constructor(props: IVoteListProps) {
super(props);
console.log(props)
}
public render(): React.ReactElement<IVoteListProps> {
// const { vote } = this.state;
return (
<VoteDetails voteDetails={this.props.adminServicePanel.getVotesInfo()} />
)
};
}
public render(): React.ReactElement<IVoteSecurityAppProps> {
return (
<main className="ui main text container">
<VoteList adminServicePanel={this.props.adminPanelService}/>
</main>
);
import {HttpClient} from '#microsoft/sp-http';
import { reject } from 'lodash';
import {IAdminPanelService} from './IAdminPanelService';
import {IReportData} from './IReportData'
import { IVoteDetailsData } from './IVoteDetailsData';
import {IVoteInfo} from './IVoteInfo'
import {VoteOptions} from './VoteOptions';
export class AdminPanelService implements IAdminPanelService {
//////////////////////////////MOCK////////////////////////////////////////////
private voteInfos : IVoteDetailsData[];
private reportData : IReportData[];
//////////////////////////////MOCK////////////////////////////////////////////
constructor(private httpClient: HttpClient, private serverRelativeSiteUrl: string) {
//MOCK
this.voteInfos = [
{
id : 1,
title : "xxx",
voteType : "xx"
},
{
id : 2,
title : "xxx",
voteType : "xxx"
}
];
}
public getVotesInfo () : Promise<IVoteDetailsData[]> {
return new Promise<IVoteDetailsData[]>((resolve : (voteMiniInfo : IVoteDetailsData[]) => void, reject : (error: any) => void): void =>{
resolve(this.voteInfos);
})
}
}
export interface IAdminPanelService {
getVotesInfo:() => Promise<IVoteDetailsData[]>;
}
import * as React from 'react';
import styles from './VoteSecurityApp.module.scss';
import { IVoteSecurityAppProps } from './IVoteSecurityAppProps';
import { escape } from '#microsoft/sp-lodash-subset';
import { VoteList } from './VoteList/VoteList';
export default class VoteSecurityApp extends React.Component<IVoteSecurityAppProps, {}> {
public render(): React.ReactElement<IVoteSecurityAppProps> {
return (
<main className="ui main text container">
<VoteList adminServicePanel={this.props.adminPanelService}/>
</main>
);
}
}
export class VoteDetails extends React.Component<IVoteDetailsProps, IVoteDetailsState>
{
state = {
vote: null,
}
// change this
componentDidMount() {
this.props.voteDetails().then(result => this.setState({
vote: result
}));
};
// rest of your codes here
}
export class VoteList extends React.Component<IVoteListProps, {}> {
constructor(props: IVoteListProps) {
super(props);
console.log(props)
}
public render(): React.ReactElement<IVoteListProps> {
// const { vote } = this.state;
return (
<VoteDetails voteDetails=
{this.props.adminServicePanel.getVotesInfo} /> // change this line
)
};
}
All errors "TypeError: Cannot read property '......' of null" in spfx components, when you call
this.state.{varname}
or
this.props.{varname}
solves one of:
Add binding 'this' in constructor to method where rise error
this.{methodname} = this.{methodname}.bind(this)
You miss initialize state in constructor (for React.Component<props,state>)
this.state = {};
You use value from props or state and miss check it for null
In question I see all of this things. For example, in this peice of code state will not be initialized, otherwise component has state
export class VoteDetails extends React.Component<IVoteDetailsProps, IVoteDetailsState>{
constructor(props: IVoteListProps) {
super(props);
console.log(props)
//this.state == null - true
}
}
Second problem is this code
<VoteDetails voteDetails={this.props.adminServicePanel.getVotesInfo()}
getVotesInfo- return promise, not data. This bad practice, use state to hold data, for example
constructor(props: ...){
super(props);
this.state{
data: null
};
this._getData = this._getData.bind(this);
}
componentDidMount(){
this._getData();
}
async _getData(){
if(this.props.adminServicePanel){
let data = await this.props.adminServicePanel.getVotesInfo();
this.setStae({data});
}
}
render():...{
const data = this.state.data;
return(
{data && data.map(...)}
);
}

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

How do you use a <Toast> in blueprintjs?

As someone who isn't using typescript, I'm having a really hard time figuring out how to use Toast. I can tell this component seems to look different than the others.
Here is the example code, what would be the ES6 equivalent?
import { Button, Position, Toaster } from "#blueprintjs/core";
class MyComponent extends React.Component<{}, {}> {
private toaster: Toaster;
private refHandlers = {
toaster: (ref: Toaster) => this.toaster = ref,
};
public render() {
return (
<div>
<Button onClick={this.addToast} text="Procure toast" />
<Toaster position={Position.TOP_RIGHT} ref={this.refHandlers.toaster} />
</div>
)
}
private addToast = () => {
this.toaster.show({ message: "Toasted!" });
}
}
TypeScript just adds type checks to the code. You can learn more about it from this question.
The change is pretty simple. You just need to remove type declarations.
import { Button, Position, Toaster } from "#blueprintjs/core";
class MyComponent extends React.Component {
refHandlers = {
toaster: (ref) => this.toaster = ref,
};
render() {
return (
<div>
<Button onClick={this.addToast} text="Procure toast" />
<Toaster position={Position.TOP_RIGHT} ref={this.refHandlers.toaster} />
</div>
)
}
addToast = () => {
this.toaster.show({ message: "Toasted!" });
}
}

Resources