I am more familiar with NodeJs than react. I have build a react component that searches for user input and provides the output in a table format based on the value that the user has typed into the search input form. This is working as I want and the code for the module is below:
import React, { Component } from 'react';
import axios from 'axios';
import Suggestions from './Suggestions';
// API url
const API_URL = 'http://localhost:3000/api/file_infos'
class Search extends Component {
state = {
query: '',
results: []
}
getCount = () => {
axios.get(`${API_URL}count?filter[where][id][regexp]=/${this.state.query}/i`)
.then(count => {
this.setState({
results: count.data
})
})
}
// query loop back API for matching queries base on text input
getInfo = () => {
axios.get(`${API_URL}?filter[where][id][regexp]=/${this.state.query}/i&filter[limit]=20`)
.then(response => {
this.setState({
results: response.data
})
})
}
// check to see if input on the search bar has changed and update the search query accordingly
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query) {
this.getInfo()
}
} else if (!this.state.query) {
}
})
}
// render form and pass results back to the home component
render() {
return (
<div>
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
</form>
<Suggestions results={this.state.results} />
</div>
)
}
}
export default Search
The second module is the suggestions module that displays the output in the table format.
The next portion of the app I am building will open a file based on the table row that the user selected. I want that table data returned to a function so that I can make an http post request to my API that will in turn open the file using a NodeJS module.
I want the suggestions component to return the value of the data items in the table cells so that the data can be used to send to the API in order to open my files. The code I have come up with so far is only returning an undefined error.
Below is what I currently have:
import React from 'react';
// return results in a table format based on the text input entered
const Suggestions = (props) => {
const state = {
results: []
}
const handleFormOpen = () => {
this.setState({
results: this.results.value
},
console.log(this.state.results)
)
}
const options = props.results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={handleFormOpen.bind(this)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))
return <table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{options}
</tbody>
</table>
}
export default Suggestions;
I am really unsure at this point if I am trying to tackle this issue in the correct way. I am thinking that maybe the suggestions component may need to be turned into a full class extending component but I am fairly lost at this point. Can someone please kindly point out my folly and get me going in the right direction?
UPDATE
As requested in the comments here is the error log from my browser:
Suggestions.js:10 Uncaught TypeError: Cannot read property 'results' of undefined
at Object.handleFormOpen (Suggestions.js:10)
at HTMLUnknownElement.callCallback (react-dom.development.js:145)
at Object.invokeGuardedCallbackDev (react-dom.development.js:195)
at invokeGuardedCallback (react-dom.development.js:248)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:262)
at executeDispatch (react-dom.development.js:593)
at executeDispatchesInOrder (react-dom.development.js:615)
at executeDispatchesAndRelease (react-dom.development.js:713)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:724)
at forEachAccumulated (react-dom.development.js:694)
at runEventsInBatch (react-dom.development.js:855)
at runExtractedEventsInBatch (react-dom.development.js:864)
at handleTopLevel (react-dom.development.js:4857)
at batchedUpdates$1 (react-dom.development.js:17498)
at batchedUpdates (react-dom.development.js:2189)
at dispatchEvent (react-dom.development.js:4936)
at interactiveUpdates$1 (react-dom.development.js:17553)
at interactiveUpdates (react-dom.development.js:2208)
at dispatchInteractiveEvent (react-dom.development.js:4913)
First thing Since your Suggestions component plays with state, I would recommend you to go with statefull component.
Stateless component is meant for getting props and returning jsx elements, there wont be any state mutations in stateless component. This is called pure function in javascript. Hope this makes clear.
Also since you declared handleFormOpen as an arrow function you no need to do binding. binding takes care automatically by arrow function. If you don't want to use arrow function and you want to bind it then do the binding always in constructor only but don't do binding anywhere in the component like you did in map.
PFB corrected Suggestions component code
import React, { Component } from 'react';
// return results in a table format based on the text input entered
export default class Suggestions extends Component {
constructor(props){
super(props);
this.state = {
results: [],
value: ""
}
}
handleFormOpen = (path, id) => {
console.log("id", id, path);//like wise pass value to this function in .map and get the value here
this.setState({
value: id
});
}
render(){
const { results } = this.props;
return (<div>
<table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{Array.isArray(results) && results.length > 0 && results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={() => this.handleFormOpen(r.OriginalPath, r.id)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))}
</tbody>
</table>
</div>)
}
}
export default Suggestions;
You are using states in Functional Component, You need to use React Component
import React from 'react';
// return results in a table format based on the text input entered
class Suggestions extends React.Component {
constructor(props) {
super(props);
this.state = {
results: [],
}
}
handleFormOpen = () => {
this.setState({
results: this.results.value
},
console.log(this.state.results)
)
}
render () {
const options = this.props.results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={handleFormOpen.bind(this)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))
return (
<table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{options}
</tbody>
</table>
)
}
}
export default Suggestions;
Related
in the code below i am fetching data from an API and want to display it on a page.
import React, { useState, useEffect } from "react";
import '../all.css';
import Axios from "axios";
const AllProduct = () => {
const [products, setProducts] = useState([]);
const fetchProducts = async () => {
const { data } = await Axios.get(
"http://localhost:8080/api/QueryAllProducts"
);
console.log(data.response);
setProducts(data.response);
console.log(products);
};
const display = () => {
return (products || []).map(product => (
<tr key={product.id}>
<th>{product.id}</th>
<th>{product.name}</th>
<th>{product.area}</th>
<th>{product.ownerName}</th>
<th>{product.cost}</th>
</tr>
) );
}
useEffect(() => {
fetchProducts();
}, []);
return (
<div>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Area</th>
<th>Owner Name</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
{display()}
</tbody>
</table>
</div>
)
}
export default AllProduct;
i did almost every method which i found on stackoverflow but still can't resolve the error. In frontend i have used ReactJS and in Backend i am using NodeJS
here is the screenshot of the error i am getting
Earlier, when one wanted to assign a default value to a variable, a common pattern was to use the logical OR operator (||):
let foo;
// foo is never assigned any value so it is still undefined
let someDummyText = foo || 'Hello!';
However, due to || being a boolean logical operator, the left hand-side operand was coerced to a boolean for the evaluation and any falsy value (0, '', NaN, null, undefined) was not returned. This behavior may cause unexpected consequences if you consider 0, '', or NaN as valid values.
I recommend change your || operator to ?? like:
(products ?? [])
because this '??' operator will conditions if you receive nullish value.
if you want to know more check it out here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
I think the challenge you are having is from your display function. You should return a Jsx element before mapping the products and enclose the mapping function in the curly ({}) bracket so React knows you are now writing javascript. So you might want to re-write your display function like below:
const display = () => {
return (<React.Fragment>
{(products || []).map(product => (
<tr key={product.id}>
<th>{product.id}</th>
<th>{product.name}</th>
<th>{product.area}</th>
<th>{product.ownerName}</th>
<th>{product.cost}</th>
</tr>}))
</React.Fragment>
);
}
Also I think you should use <td>{product.name}</td>... to return table data and not <th>{product.name}</th>
app.component.ts
Debugger given the error that this.task is undefined
updateTodo(task: any){
this.todoService.updateData(task._id, this.task).subscribe(res => {
this.data= res;
console.log(res);
console.log(this.task);
});
}
app.service.ts
This is service file where the backend api are call in my angular app
updateData(id: any , data: any){
return this.httpClient.put('http://localhost:3000/todos/'+id, data);
}
app.component.html
This is the frontend of my app where the todos show and others user interface
<tbody>
<tr *ngFor="let todo of tasks ; let i = index">
<td>{{todo.todos}}</td>
<td> <input type="checkbox" (change)="updateTodo(todo)"[checked]="todo.isDone</td>
<td>
<button class="btn btn-danger btn-sm" id="del-btn"
(click)="deleteData(todo._id)">Delete</button>
</td>
</tr>
</tbody>
app.model.ts
This is model file
export class Todo {
_id:any;
todos:any;
isDone:any;
}
backend api
This is the backedn function which i created to update my todo
router.put('/:id' , async (req,res) => {
const id = req.params.id;
if(ObjectId.isValid(id)){
const todo = (req.body);
const todoUpdate = await Todo.findByIdAndUpdate(id ,{$set:emp}, {new:true});
res.status(200).json({code:200, message:'Todo Updated Successfully'});
}
else{
res.status(400).send('Todo Not Found By Given Id' + id);
}
});
I'm not sure if we understood each other, but you are passing the task as a parameter but then on two occasions you are trying to use the value of this.task. They are not the same thing and if this.task is not initialized then of course it will show that it's undefined.
updateTodo(task: any) {
console.log('task:', task); // Is the task correct?
this.todoService.updateData(task._id, task).subscribe(res => {
this.data = res;
console.log(res);
console.log(task); //not this.task
});
}
EDIT:
If the DB is not updated you might be sending incorrect data there. If there are no errors on Angular side you have to check the Back-End side.
I solve this question to add [(ngModel)]="todo.isDone" in my checkbox input filed
<tbody>
<tr *ngFor="let todo of tasks ; let i = index">
<td>{{todo.todos}}</td>
<td> <input type="checkbox" (change)="updateTodo(todo)" [(ngModel)]="todo.isDone</td>
<td>
<button class="btn btn-danger btn-sm" id="del-btn"
(click)="deleteData(todo._id)">Delete</button>
</td>
</tr>
And In my app.component.ts
updateTodo(task: any) {
this.todoService.updateData(task._id, task).subscribe(res => {
this.data = res;
console.log(res);
console.log(task);
});
}
I have configured a react website to receive json data and store it into an array in the format depicted in the attached image. How would I go about displaying this information in a table format?json data stored in array
If you don't want to use property names, you could do something like that :
import React, { Component } from "react";
import { render } from "react-dom";
const App = () => {
const data = [
{ id: 0, value: "item 1" },
{ id: 1, value: "item 2" },
{ id: 2, value: "item 3" }
];
const keys = Object.keys(data[0]);
return (
<div>
<table>
<thead>
{keys.map(key => {
return (
<th>
<td>{key}</td>
</th>
);
})}
</thead>
<tbody>
{data.map((item, index) => {
return (
<tr>
{keys.map(key => (
<td>{item[key]}</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
);
};
render(<App />, document.getElementById("root"));
Note that all of your items need to have the same properties (here 'id' and 'value') for this to work.
Here is the repro on stackblitz
If you need something better then you should look for a package made for this, there's plenty on internet.
I'm using React Js as frontend and node express as backend. When I start the my frontend using npm start it runs without errors but when I start my backend too then it throws this error: TypeError: Cannot read property 'map' of undefined and I could not find any solution for this.
Here's the code where the error is:
import React, { Component } from 'react'
import ApiService from "../../service/ApiService";
import SearchField from "./SearchField";
class ListModuleComponent extends Component {
constructor(props) {
super(props)
this.state = {
modules: [],
message: null
}
this.deleteModule = this.deleteModule.bind(this);
this.editModule = this.editModule.bind(this);
this.addModule = this.addModule.bind(this);
this.reloadModuleList = this.reloadModuleList.bind(this);
}
componentDidMount() {
this.reloadModuleList();
}
reloadModuleList() {
ApiService.fetchModules()
.then((res) => {
this.setState({ modules: res.data.result })
});
}
deleteModule(moduleId) {
ApiService.deleteModule(moduleId)
.then(res => {
this.setState({ message: 'Delete successful.' });
this.setState({ modules: this.state.modules.filter(module => module.id !== moduleId) });
})
}
editModule(id) {
window.localStorage.setItem("moduleId", id);
this.props.history.push('/edit-module');
}
addModule() {
window.localStorage.removeItem("moduleId");
this.props.history.push('/add-module');
}
render() {
return (
<div>
<h2 className="text-center">Modul data</h2>
<button className="btn btn-danger" style={{ width: '100px' }} onClick={() => this.addModule()}>Add</button>
<SearchField />
<table className="table table-striped">
<thead>
<tr>
<th className="hidden">Id</th>
<th>Id</th>
<th>Name</th>
{/* <th>Key</th> */}
<th>Default value</th>
<th>Description</th>
<th>State</th>
</tr>
</thead>
<tbody>
{
this.state.modules.map( // Error throws on this line //
module =>
<tr key={module.id}>
<td>{module.moduleName}</td>
<td>{module.moduleDefaultValue}</td>
<td>{module.description}</td>
<td>{module.isActive}</td>
<td>
<button className="btn btn-success" onClick={() => this.deleteModule(module.id)}> Delete</button>
<button className="btn btn-success" onClick={() => this.editModule(module.id)} style={{ marginLeft: '20px' }}>Edit</button>
</td>
</tr>
)
}
</tbody>
</table>
</div>
);
}
}
export default ListModuleComponent;
So the error only show up if my backend starts and I don't know if it is caused by backend or frontend, only see the error for the React frontend side. I can provide more code if needed.
res.result.data is most likely undefined. You could solve this in 2 ways:
1. Prevent undefined in your state.
You could set your state to an empty array in case res.result.data is undefined like so:
this.setState({ modules: res.data.result || [] });
This will make sure your state always contains an array even if the backend provides no data.
2. Do a null-check before rendering your data.
Check if your this.state.modules holds a value before rendering your content.
<div>
{this.state.modules && this.state.modules.map(module => {
// Code
}}
</div>
The default state for modules is an array, this means that mapping over it is okay, so if it doesnt fetch any data then the component will render just fine.
this.state = {
modules: [],
message: null
}
When the backend has been started the reload modules method is setting the modules to be res.data.result
The error is Cannot read property 'map' of undefined.
Because of this I think res.data.result is undefined.
to fix this check what res is and make sure the thing you are setting in state is the array of data
Seems like a null check would fix the obvious problems here.
{
this.state.modules instanceof Array && this.state.modules.map(
module => { /*...*/ }
)
}
Nevertheless, I strongly assume that the backend has already delivered the data correctly. Also your code has an assumption that the array is always there and I cannot find a place where it has been deleted/false initialized. So I Just assume that the backend returns you an undefined/null value, which would also be covered by this check. So maybe investigate your backend whether it returns a null value.
I have the code of my component like :
import React, { Component } from 'react';
import axios from 'axios';
import "bootstrap/dist/css/bootstrap.min.css";
import {
Table
} from 'reactstrap';
const Adhoc = async (props) => {
let cost = await props.cost(props.adhoc._id)
return(
<tr>
<td>{props.adhoc.jIssue}</td>
<td>{props.adhoc.paid ? "Paid" : "Not paid"}</td>
<td>APP{props.adhoc.sprint}</td>
<td>£{cost.data[0]}</td>
</tr>
)}
export default class QAdhocsDisplay extends Component {
constructor(props) {
super(props);
this.costingAdhoc = this.costingAdhoc.bind(this)
this.state = {
adhocs: []
};
}
componentDidMount() {
axios.get('http://localhost:5000/adhocs/retrieve')
.then(response => {
this.setState({ adhocs: response.data })
})
.catch((error) => {
console.log(error);
})
}
async costingAdhoc(id) {
const data = await axios.get('http://localhost:5000/jira/issue/' + id)
.catch((error) => {
console.log(error);
})
return data;
}
adhocsList() {
return this.state.adhocs.map(currentadhoc=> {
return <Adhoc adhoc={currentadhoc} cost={this.costingAdhoc} key={currentadhoc._id}/>;
})
}
render(){
return (
<div className="toborder" style = {{paddingBottom: "59px"}}>
<div className="display" style ={{backgroundColor: "#5394b2"}}>
<h5 style = {{padding:"13px"}}>Adhoc status</h5>
</div>
<div className="table">
<Table size="sm" bordered striped>
<thead className="thead-light">
<tr className="adhocs">
<th className="sticky-column medium" >Adhoc issue</th>
<th className="sticky-column medium" >Payment status</th>
<th className="sticky-column medium" >Sprint</th>
<th className="sticky-column medium" >Projected cost</th>
</tr>
</thead>
<tbody>
{ this.adhocsList() }
</tbody>
</Table>
</div>
</div>
)}
}
My issue is that I have the function costingAdhoc(id) which I pass as a prop to the child component Adhoc. To be able to access the information from the axios call I need these both functions to be async.
A child component of type Adhoc will be rendered for each item in the state that will get mapped in the function adhocsList(). For some reason this causes the axios call in the componentDidUpdate() to throw this error:
Objects are not valid as a React child (found: [object Promise]). If
you meant to render a collection of children, use an array instead.
And the error points to the line where I set the state. This means that the costingAdhoc(id) function async nature causes my axios call in the componentDidUpdate() function only return a promise and not the actual data.
Your problem that is Adhoc component which you're declaring as a child component is actually a promise.
Why?
A function that is declared as async returns promise by default, so even if you return:
<tr>
<td>{props.adhoc.jIssue}</td>
<td>{props.adhoc.paid ? "Paid" : "Not paid"}</td>
<td>APP{props.adhoc.sprint}</td>
<td>£{cost.data[0]}</td>
</tr>
you're actually returning it wrapped inside a promise. React components cannot be promises.