My component:
interface Props extends RouteComponentProps<any> {
sample: {
info: SampleInfo;
};
}
export class Step extends Component<Props, State>{
render() {
const { title } = this.props.sample.info;
return (
<TextContent >
<Title headingLevel="h1" size="3xl">
Uploading and validating your swagger : {name} // name is available under sample.info
</Title>
</TextContent>
)}
}
When I am trying to access props, I am getting this.props.sample is undefined.
I am trying to call this Step Component in a wizard:
const steps = [
{
id: 1,
name: "show",
component: <Form />,
},
{
id: 2,
name: "Listing",
component: <Sample2 />,
},
{ name: "Save", component: <Step/>}, // I am calling my Step component from here
];
Do I need to pass anything in Step component, please help me with this. I am new to reactjs
You <Step/> component must actually have props.
It must pass something like <Step sample={{ info: { title: 'whatever' } }}/> then it'll work.
RouteComponentProps from react-router is defined as:
export interface RouteComponentProps<
Params extends { [K in keyof Params]?: string } = {},
C extends StaticContext = StaticContext,
S = H.LocationState
> {
history: H.History<S>;
location: H.Location<S>;
match: match<Params>;
staticContext?: C;
}
(Source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-router/index.d.ts#L70)
Due to extends { [K in keyof Params]?: string } and you specifying
Params generic as any (via RouteComponentProps<any>), K may be any key and because of the ? the value for all keys may be undefined.
I do not use react-router but I think you have to specify RouteComponentProps using appropriate values fot the generic parameters.
Related
I am passing array of object as a prop from App.js -> searchResult -> TrackList.js. But when I apply .map function on the array of object it shows Cannot read property 'map' of undefined . I have tried different ways to solve this but none of them worked. In console I am getting the value of prop. And my TRackList.js component is rendering four times on a single run. Here is the code
App.js
this.state = {
searchResults: [
{
id: 1,
name: "ritik",
artist: "melilow"
},
{
id: 2,
name: "par",
artist: "ron"
},
{
id: 3,
name: "make",
artist: "zay z"
}
]
return ( <SearchResults searchResults={this.state.searchResults} /> )
In Searchresult .js
<TrackList tracked={this.props.searchResults} />
In TrackList.js
import React from "react";
import Track from "./Track";
export default class TrackList extends React.Component {
constructor(props) {
super();
}
render() {
console.log("here", this.props.tracked);
return (
<div>
<div className="TrackList">
{this.props.tracked.map(track => {
return (<Track track={track} key={track.id} />);
})}
</div>
</div>
);
}
}
Here is the full code -- https://codesandbox.io/s/jamming-ygs5n?file=/src/components/TrackList.js:0-431
You were loading the Component TrackList twice. One time with no property passed, that's why it was first set in console then looks like it's unset, but it's just a second log. I have updated your code. Take a look here https://codesandbox.io/s/jamming-ddc6l?file=/src/components/PlayList.js
You need to check this.props.tracked.map is exists before the loop.
Solution Sandbox link:https://codesandbox.io/s/jamming-spf7f?file=/src/components/TrackList.js
import React from "react";
import Track from "./Track";
import PropTypes from 'prop-types';
export default class TrackList extends React.Component {
constructor(props) {
super();
}
render() {
console.log("here", typeof this.props.tracked);
return (
<div>
<div className="TrackList">
{this.props.tracked && this.props.tracked.map(track => {
return <Track track={track} key={track.id} />;
})}
</div>
</div>
);
}
}
TrackList.propTypes = {
tracked: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
artist: PropTypes.string,
}))
};
You need to check this.props.tracked value before implementing the map function.
you can simply check using this.props.tracked && follow your code.
You should add searchResults={this.state.searchResults} in your app.js to Playlist, take it in Playlist with props, and then set it in TrackList from Playlist (tracked={props.searchResults}).
Also, Typescript helps me not to do such mistakes.
Also, add a key prop to your component that you return in the map function.
I'm using React and TypeScript and trying to pass some data like prop to child component and use it in child component. But i'm getting error and i can't understand why is happened and how fix it. I'm beginner on TypeScript also.
Here is my parent component
import * as React from "react";
import ChildComponent from "./ChildComponent";
const data = [
{
title: "A",
id: 1,
},
{
title: "B",
id: 1,
},
];
const ParentComponent = () => {
return (
<ChildComponent items={data} />
)
}
export default ParentComponent;
Here is error in parent component at items
(JSX attribute) items: {
title: string;
id: number;
}[]
Type '{ items: { title: string; id: number; }[]; }' is not assignable to type 'IntrinsicAttributes'.
Property 'items' does not exist on type 'IntrinsicAttributes'.ts(2322)
And when in regular react and es6 i can use this props in child component like this:
const ChildComponent = (props) => {
return (
<div>
{props.items.map((item) => (
<p to={item.title}></p>
))}
</div>
)
}
but have use this props in child component if it will be TypeScript?
You need to specify what type of props the child component wants. For example:
interface Item {
title: string;
id: number;
}
interface ChildComponentProps {
items: Item[]
}
const ChildComponent: React.FC<ChildComponentProps> = (props) => {
return (
<div>
{props.items.map((item) => (
<p to={item.title}></p>
))}
</div>
)
}
Added to the reply, if the props can be null, you put a question mark.
interface ChildComponentProps {
items?: Item[]
}
In the following two codes, There is a child and a parent component. The child component has a TextField, and the parent component has Dropdown, is there a way I can set the TextField's value to whatever I change the Parent Dropdown to? For example, if I selected "Medium", I want to change the child component TextField's value to "Medium". Is this possible?
Here is the Child Component
import * as React from "react";
import { TextField } from 'office-ui-fabric-react/lib/';
const ChildComponent = (props) => {
return(
<TextField
label="Description" required
key="descriptionKey"
styles={props.styles}
value={props.value}
multiline={props.multiline}
onChange={props.onChange}
defaultValue={props.defaultValue}
placeholder={props.placeholder}
/>
);
}
export default ChildComponent;
Here is the Parent Component
import * as React from "react";
import ChildComponent from './ListItem';
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/';
class ParentComponent extends React.Component {
handleChange = (event) => {
this.setState({
operationType: event.target.value,
})
};
render(){
const riskAssess: IDropdownOption[] = [
{ key: 'high', text: 'High' },
{ key: 'medium', text: 'Medium' },
{ key: 'low', text: 'Low' },
]
return(
<div>
<Dropdown
label="Risk Assessment" required
ariaLabel="Risk"
styles={{ dropdown: { width: 125 } }}
options={riskAssess}
onChange={this._onChange}
/>
<ChildComponent value="This is what I want to change to Dropdown value" />
</div>
);
}
private _onChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
this.setState({operationType: item.text})
console.log(event);
};
}
export default ParentComponent;
Yep! Replace "This is what I want to change to Dropdown value" with {this.state.operationType}. The curly braces tell JSX that you want a variable to populate that value.
With TypeScript, your components need to define their property and state structures, by adding generic arguments to React.Component:
interface ParentProps = {};
interface ParentState = {
operationType: string;
};
class ParentComponent extends React.Component<ParentProps, ParentState> {
For ChildComponent, you'd want to extend React.FC (short for FunctionalComponent) in a similar way. Since it doesn't define any state, it can forego the second generic argument:
interface ChildProps {
styles?: React.CSSProperties;
value?: string;
multiline?: boolean;
onChange?: React.ChangeEventHandler<HTMLInputElement>
defaultValue?: string;
placeholder?: string;
};
const ChildComponent: React.FC<ChildProps> = (props) => {
Now TypeScript knows what to expect.
TypeScript's precise interface definitions ensure that each component gets only what it allows. It replaces React's PropTypes warnings with enforced compilation errors. It's more complex, having to define all this, but it makes for less error-prone development.
I'm having a rather strange issue. I'm using TypeScript and Preact to create a Button component. I'm using a TypeScript interface for prop validation, essentially and using a constant variable as the "default" props. The props are then applied using Object.assign and passed to super. Here is the code as follows:
import classNames from "classnames";
import { Component, h } from "preact";
export interface ButtonProps {
color?: string;
type?: "primary" | "secondary";
}
const ButtonPropsDefaults: ButtonProps = {
color: "primary",
type: "primary",
};
export default class Button extends Component<PButtonProps> {
constructor(props: ButtonProps) {
// Logs { color: "primary", type: "primary" }
console.log(ButtonPropsDefaults);
super(Object.assign(ButtonPropsDefaults, props));
// Logs { color: "primary", type: "primary", children: {...} }
console.log(this.props);
}
public render() {
// Logs { children: {...} }
console.log(this.props);
const className = classNames({
"button": true,
[`button--color-${this.props.color}`]: !!this.props.color,
[`button--type-${this.props.type}`]: !!this.props.type,
});
return <button className={className}>{this.props.children}</button>;
}
}
Notice the results of the console.log statements. It seems like the value "reverts" back to the original state as passed in to the constructor. So, for example, if I were to use the component as <Button color="primary">Test</Button>, the console.log statement in the render function will be { color: "primary", children: {...} }.
Thanks for reading and I appreciate any help you can give!
You want merge the props and your default props into a new object. So you can do
super(Object.assign({}, ButtonPropsDefaults, props)) or
super({...ButtonPropsDefaults, ...props})
Say you define your component like so:
interface IProps {
req: string;
defaulted: string;
}
class Comp extends React.Component<IProps, void> {
static defaultProps = {
defaulted: 'test',
};
render() {
const { defaulted } = this.props;
return (
<span>{defaulted.toUpperCase()}</span>
);
}
}
when you want to use it, TypeScript wants the defaulted prop from you, even though it's defined in defaultProps:
<Comp req="something" /> // ERROR: TypeScript: prop 'defaulted' is required
However, if you define the props interface like so:
interface IProps {
req: string;
defaulted?: string; // note the ? here
}
then you cannot use it in:
render() {
const { defaulted } = this.props; // ERROR: prop 'defaulted' possibly undefined
return (
<span>{defaulted.toUpperCase()}</span>
);
}
How to define the IProps, defaultProps and component correctly so the types make sense?
EDIT:
I'm using the strictNullChecks flag.
I have an example with the following code (ComponentBase is just my wrapper around React.Component).
Edit: updated code to work with 'strictNullChecks' setting
interface IExampleProps {
name: string;
otherPerson?: string;
}
/**
* Class with props with default values
*
* #class Example
* #extends {ComponentBase<IComponentBaseSubProps, {}>}
*/
export class Example extends ComponentBase<IExampleProps, {}> {
public static defaultProps: IExampleProps = {
otherPerson: "Simon",
name: "Johnny"
};
constructor(props: IExampleProps) {
super(props);
}
public render(): JSX.Element {
const person: string = this.props.otherPerson === undefined ? "" : this.props.otherPerson;
return(
<div>
<h1><small>Message by ComponentBaseSub: Hello {this.props.name} and {person} </small></h1>
</div>
);
}
}
I have no issues using Visual Studio Code, TypeScript 2.0.3, TSLint 0.5.39.
Even simpler is
<span>{(defaulted as string).toUpperCase()}</span>
Works the same way with properties. If Foo requires the barProp property but Parent does not and gets it through defaultProps, Parent's render method can do
<Foo barProp={this.props.barProp as string} />
If you know for sure the prop will have a default value, you can use the null assertion type operator, like this:
render() {
const { defaulted } = this.props;
return (
<span>{defaulted!.toUpperCase()}</span>
);
}