I have a simple React component that should retrieve some objects from an api and display them on screen. The problem is, the property holding my data always seems to have an undefined value. In C#, I have a Property class:
public class Property
{
public int PropertyID { get; set; }
public string Name { get; set; }
}
My component's parent gets a list of these from an api and passes them in as props.
Here's my component:
export interface IPropertySearchResult {
PropertyID: number,
Name: string
}
export interface IPropertySearchResults {
Results: IPropertySearchResult[]
}
export interface IPropertyWindowProps {
Properties: IPropertySearchResults
}
export interface IPropertyWindowState {
}
export class PropertyWindow extends React.Component<IPropertyWindowProps,
IPropertyWindowState> {
constructor(props: IPropertyWindowProps) {
super(props);
}
render() {
return (
<div >
<ul>
{
this.props.Properties && this.props.Properties.Results ?
this.props.Properties.Results.map((p: IPropertySearchResult, idx: number) =>
<li key={idx}> <PropertyEditor Property={p} /> </li>)
: 'properties null'
}
</ul>
</div>
);
}
}
As shown in the image below, this.props.Properties contains the objects I need but for some reason this.props.Properties.Results is always marked as undefined.
.
Update
I think the problem has something to do with the way I'm reading the data. I have my controller:
[HttpGet("Search")]
public IActionResult GetProperties()
{
var props = new Property[]
{
new Property(){PropertyID=1, Name="default1"},
new Property(){PropertyID=2, Name="default2"},
new Property(){PropertyID=3, Name="default3"},
};
return Ok(new { Properties = props });
}
}
And its client:
export class PropertyReader {
public static search(): Promise<IPropertySearchResults> {
return new Promise<IPropertySearchResults>((resolve, reject) => {
axios.get(`api/Settings/Search`)
.then(res => {
resolve(res.data);
});
});
}
}
Then my component's parent calls the client:
componentDidMount() {
PropertyReader.search()
.then(p => this.setState({ properties: p }));
}
For some reason, it's creating an IPropertySearchResults and putting the data into a dynamically added array rather than into the Results array.
It turns out, this had a really silly solution. The values were getting save to a property with a lower case name. I had to change
export interface IPropertySearchResults {
Results: IPropertySearchResult[]
}
to
export interface IPropertySearchResults {
results: IPropertySearchResult[]
}
Related
Today I started using MobX and the first problem I ran into is how to execute a function in a React class component whenever an Observable updates.
I am under the impression this can be achieved using a reaction, but I'm not sure how to make it work.
class MissionLog {
private _missions: Array<IMissionItem> = [];
public get missions() {
return this._missions;
}
constructor() {
makeAutoObservable(this);
}
// Example of a method that modifies the _missions array
public receiveMission(mission: IMissionItem) {
this._missions.push(mission);
}
}
export const missionLog = new MissionLog();
// Example of modifying the missions array
missionLog.receiveMission(someMission);
export const ObserverTest = observer(class _ObserverTest extends React.Component {
constructor(props: any) {
super(props);
// Executes the console.log at the start,
// but not when missionLog.missions changes.
autorun(() => {
console.log("Autorun", missionLog.missions);
})
// Never executes the console.log
reaction(
() => missionLog.missions,
(mission) => {
console.log("Reaction");
}
)
}
render() {
return (
// Accessing missionLog.missions here
// gives me the correct, updated data,
// so my setup should be fine.
)
}
});
I also tried to use intercept and observe instead of reaction, but also no result.
I am a little confused on the idea of using props in the context I am using for my React app. In my component, I need to check if the value of a certain prop (props.companyCode) matches a certain string, and only then will it print out a <p> of what I need. Below is what I have for calling the prop in the component:
Components/CompanyContact.jsx
class CompanyContact extends React.Component {
help() {
if (this.props.companyInfoList.companyCode === '1234') {
return <p>something</p>;
}
return <p>somethingelse</p>;
}
render() {
const help = this.help();
return (
<div>
{help};
</div>
)}}
export default CompanyContact;
And this is what I have for the container:
Container/InfoContainer.jsx
class InfoContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
companyInfoList: null,
};
}
async componentWillMount() {
const companyInfoCachedData = CachingService.getData('companyInfoList');
if (companyInfoCachedData) {
this.setState({ companyInfoList: companyInfoCachedData });
return;
}
}
async getCompanyInfo(accessToken) {
try {
const companyProfileResponse = await requestAWSGet('api/company-profile', undefined, accessToken);
CachingService.setData('companyInfoList', companyProfileResponse);
this.setState({ companyInfoList: companyProfileResponse });
} catch (err) {
throw err;
}
}
render() {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
}
}
export default InfoContainer;
Nothing is returned when I run the application and I believe it's because I'm not calling the prop correctly in my component but I am unsure as to how to go about fixing it. I'm fairly new to working with props so still trying to get my bearings.
I'm assuming you are getting an error somewhere because of this not having props and this.props.companyInfoList.companyCode trying to access a property on a non object. this.props.companyInfoList is initially set to null so accessing a property on it will break.
A few strategies to fix the problem:
Default it to an empty object
this.state = {
companyInfoList: {},
}
Block the rendering of the component until it has a value:
if (this.state.companyInfoList) {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
} else {
return null;
}
Check that the prop is an object and has the key companyCode on it:
if (this.props.companyInfoList &&
this.props.companyInfoList.companyCode &&
this.props.companyInfoList.companyCode === '1234') {
In addition, this will be in the wrong context and the changes above will most likely no be enough. Try changing to an arrow function like this:
help = () => {
// your code here
}
I would personally refactor that component logic and directly use the prop value inside the render method like:
class CompanyContact extends React.Component {
render() {
const { companyInfoList } = this.props;
return companyInfoList && companyInfoList.companyCode === '1234' ? (
<p>something</p>
) : (
<p>somethingelse</p>
)
}
}
export default CompanyContact;
I am building a React Typescript app, and using CKEditor, a method gives me this object :
And when i try to render it, it gives me this error :
Can someone explain to me what am i doing wrong ?
Here's the code :
export default class Toolbar extends React.Component<IProps, IState> {
public toolbar: ToolbarView | undefined
public componentDidMount() {
this.toolbar = new ToolbarView()
this.toolbar.render()
console.log(this.toolbar.element) // the result is the first screenshot
}
public render() {
return this.toolbar ? this.toolbar.element : null
}
}
Actually, i was able to achieve this using ReactRef:
public toolbarRef: React.RefObject<HTMLDivElement> = React.createRef()
public componentDidMount() {
// this.editor initialisation
this.toolbar = new ToolbarView()
// adding stuff to the toolbar
this.toolbar.render()
const el = ReactDOM.findDOMNode(this.toolbarRef.current as HTMLDivElement)
if (el instanceof Element) {
el.appendChild(this.toolbar.element)
}
}
public render() {
return (
<div ref={this.toolbarRef} />
)
}
I have a class level variable that I want to be able to access in my functions. I thought this worked like a global variable but I'm new to this. This is the simplified code I have right now that is not working:
class People extends React.Component<PeopleProps, {}> {
public isAdmin: boolean = false;
public render() {
return <div onClick={this._checkAdmin}>Call function</div>;
}
private _checkAdmin() {
if (this.isAdmin) {
console.log("is admin");
}
}
}
The error occurs on the if statement. The error I am getting is Uncaught
TypeError: Cannot read property 'isAdmin' of null
I am assuming that this is undefined but I'm not sure how that could happen. There are no errors in VS before building this. I can get to the _checkAdmin function so it seems like this is working there. Do I need to pass an argument to '_checkAdmin'?
Missed binding this
class People extends React.Component<PeopleProps, {}> {
public isAdmin: boolean = false;
public render() {
return <div onClick={this._checkAdmin.bind(this)}>Call function</div>;
}
private _checkAdmin() {
if (this.isAdmin) {
console.log("is admin");
}
}
}
As said cooper, you forgot bind this. But you don't care about this if you use arrow functions:
class People extends React.Component<PeopleProps, {}> {
public isAdmin: boolean = false;
public render() {
return <div onClick={this._checkAdmin}>Call function</div>;
}
private _checkAdmin = () => {
if (this.isAdmin) {
console.log("is admin");
}
}
}
Getting started with React and Redux and I have a method in my class to create new lines in a piece of text:
class JobPage extends React.Component {
componentWillMount() {
this.props.dispatch(fetchJob(this.props.match.params.slug))
}
getMultiLineText(text) {
text.split('\n').map((item, key) => {
return <span key={key}>{item}<br/></span>
})
}
render() {
const { job } = this.props;
return(
{this.getMultiLineText(job.desc)}
)
}
}
Within my render() method I try and call it using the following but the param is not passed. It's null:
{this.getMultiLineText(job.desc)}
If I just do...
{job.desc}
it prints the description.
The value is probably null during the initial render.
You can solve it for example as
{this.getMultiLineText(job.desc || '')}
You can also fix it in your reducer.