Reactjs - this.state.data.map arguments is UNDEFINED - node.js

ive been trying to fetch data from my api but its somehow confusing that this.state.Data.map does work but the argument dynamicdata is undefined.
in App.js react
class ShowAll extends Component {
constructor(props){
super(props);
this.state = {
Data: [],
}
}
componentDidMount(){
Request.get('/budget').then((res)=>{
let DataString = JSON.stringify(res.body);
this.setState({
Data: DataString
}, function(){
console.log(DataString);
})
}).catch((err)=> console.log(err));
}
render(){
return(
<div>
{
this.state.Data.map(function(dynamicData, key){
<div>{dynamicData[0]._id}</div> // doesn't render anything and throws an error message saying TypeError: Cannot read property '_id' of undefined
})
}
</div>
)
}
}
**EDIT 1 **
The api data structure is
[{
"_id":"lul",
"_creator":"5a8f8ecdd67afa6494805bef",
"firstItem":"hero",
"secondItem":"30",
"thirdItem":"3",
"__v":0,
"tBudget":9,
"thirdPrice":3,
"secondPrice":3,
"firstPrice":3
}]

Oh. Your original post was a completely different problem.
Two problems. First:
You're mapping, so you don't need to index the mapped value
this.state.Data.map(function(dynamicData, n) {
// dynamicData _is_ the nth element in the array, you don't need dynamicData[x]
dynamicData._id === "lul"
})
second, you're not returning anything from your map callback
map(function(...) {
return (<div>...</div>)
})

Based on the server response, you don't need to access the item 0 when rendering each one of them
class ShowAll extends Component {
constructor(props){
super(props);
this.state = {
Data: [],
}
}
componentDidMount(){
Request.get('/budget').then((res)=>{
this.setState({
Data: res.body, // Assuming res.body is already an array
}, function(){
console.log(DataString);
})
}).catch((err)=> console.log(err));
}
render(){
return(
<div>
{
this.state.Data.map((dynamicData, key) => // Using an arrow function
<div>{dynamicData._id}</div> // Don't access the item 0
)
}
</div>
)
}
}
The previous code assumes res.body is already an array. If that's not the case and you are actually getting a string or something else, you need to parse the response and make sure you assign an array to the state.

Related

React component not rendering after state change

As the title says, when my state changes in my component, the sub components aren't rerendering.
class App extends Component {
constructor() {
super()
this.state = {
url: ""
}
this.handleWorkerSelect = this.handleWorkerSelect.bind(this)
}
handleWorkerSelect(url) {
this.setState({ url })
}
render() {
return (
<div className="App">
<Workers className="workers" handleClick={this.handleWorkerSelect}/>
<HermesWorker url={this.state.url}/>
</div>
)
}
}
const Workers = (props) => {
return (
<div>
<button onClick={() => props.handleClick("http://localhost:5000/api")}>Worker 1</button>
<button onClick={() => props.handleClick("http://localhost:2000/api")}>Worker 2</button>
</div>
)
}
export default App
here is hermesworker.js
class HermesWorker extends Component {
constructor() {
super()
this.state = {
items: [],
visited: [{name: "This Drive", path: "#back", root: ""}]
}
this.handleFolderClick = this.handleFolderClick.bind(this)
this.handleFileClick = this.handleFileClick.bind(this)
}
componentDidMount() {
if (this.props.url.length === 0) return
fetch(this.props.url)
.then(res => res.json())
.then(items => this.setState({ items }))
}
render() {
const folders = this.state.items.map((item) => {
if (!item.isfile) {
return <Card handleClick={this.handleFolderClick} root={item.root} path={item.path} isfile={item.isfile} name={item.name} size={item.size}/>
}
})
const files = this.state.items.map((item) => {
if (item.isfile) {
return <Card handleClick={this.handleFileClick} root={item.root} path={item.path} isfile={item.isfile} name={item.name} s ize={item.size}/>
}
})
const pathButtons = this.state.visited.map((item) => {
return <PathButton handleClick={this.handleFolderClick} root={item.root} path={item.path} name={item.name}/>
})
return (
<div>
{pathButtons}
<div className="flex-container">
{folders}
{files}
</div>
</div>
)
}
}
Essentially the issue is that the HermesWorker component is not being rerendered to use the new url prop. I am not sure why this is happening because for example, in the hermesworker it renders other subcomponents that do get rerendered during a state change.
Any information is appreciated
EDIT updated to add hermes worker, the file is over 100 lines so i cut out and only pasted the stuff I thought was important to the issue, can supply more if needed
I tested that code and it seems to be working fine. Could you provide What is set in HermesWorker component?
Edit: You'll require to set your state with setState on component updates. To do this, you may look for componentDidUpdate, which will run on every update. This is different from componentDidMount, which (hopefully) will run once and then the component may update and re-render, but re-render it's not considered as "mount". So you may try this instead:
constructor(props) {
super(props);
this.state = {
url: '',
items: [],
visited: [{name: "This Drive", path: "#back", root: ""}]
}
this.fetchData = this.fetchData.bind(this);
}
componentDidMount() {
//Mount Once
}
componentDidUpdate(prevProps, prevState) {
if (this.state.url !== this.props.url) {
this.setState({url: this.props.url});
// Url state has changed.
}
if(prevState.url !== this.state.url){
//run your fetch
this.fetchData();
}
}
fetchData(){
if (this.props.url.length === 0) return
fetch(this.props.url)
.then(res => res.json())
.then(items => this.setState({ items }));
}
Note: I moved the fetch to its own function, but that's completly up to you.
Also notice i added url to the state. Make sure to keep your props set to avoid unexpected behaviours.
Edit 2: componentDidUpdate will hand you prevProps and prevState as parameters. With prevProps you get access to whatever props you got on the previous update, and with prevState, as you may guess, you get access to whatever-your-state-was on the previous update. And by "on the previous update" i mean before the update got executed.

How to display data read from a file using Electron and React?

My goal is simply to display data (using React) from a file stored locally with my Electron app. I've gotten halfway there in actually reading and processing the data, I just can't figure out how to display it.
Here's what I have for my file read:
export function read() {
let values = [];
fs.readFile(
path.resolve(__dirname, './files/test.txt'),
'utf-8',
(err, data) => {
if (err) throw err;
values = data.toString().split('\n');
const listItems = values.map(val => <p>{val}</p>);
return listItems;
}
);
}
This works correctly, and I've console logged all the correct values.
The part that's confusing me is when I want to display it. Here's my react component:
// #flow
import React, { Component } from 'react';
import styles from './ReadFile.css';
import { read } from '../actions/fileread';
type Props = {};
export default class ReadFile extends Component<Props> {
props: Props;
render() {
const result = read();
return (
<div className={styles.container} data-tid="container">
<p>Read from File</p>
{result}
</div>
);
}
}
What I would expect this to do is call the read function, store it in result and then print the results with {result}. What it does instead is display nothing. It also gives no errors.
I have a feeling this has to do with some odd server/client relationship between the react frontend and the node.js "backend" reading the file. I'm not sure how to create a simple interface between these two components to get them to work.
As mentioned in my comment your code is async and your read() method is not returning anything. You should have something close to this:
export default class ReadFile extends Component<Props> {
props: Props;
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
read((result) => {
this.setState({
result,
});
});
}
render() {
return (
<div className={styles.container} data-tid="container">
<p>Read from File</p>
{this.state.result}
</div>
);
}
}
And for read() this:
export function read(callback) {
let values = [];
fs.readFile(
path.resolve(__dirname, './files/test.txt'),
'utf-8',
(err, data) => {
if (err) throw err;
values = data.toString().split('\n');
const listItems = values.map(val => <p>{val}</p>);
return callback(listItems);
}
);
}

Performing a fetch request only on state update

I'm new to React and I'm trying to figure out how to work with fetch correctly.
I have a React component that I'd like to update from a remote server whenever its parent's state updated.
i.e - parent's state changed -> myComponent calls remote server and re-renders itself.
I've tried the following:
If I only perform the .fetch call on componentDidMount, it disregards any state updates.
If I perform the .fetch call on componentDidUpdate as well it calls the server endlessly (I assume because of some update-render loop)
I have tried using the componentWillReceiveProps function, and it works, but I understand it's now deprecated.
How can I achieve this kind of behavior without componentWillReceiveProps ?
class myComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
images: []
};
}
componentDidMount() {
let server = "somethingserver.html";
fetch(server)
.then(res => {
if (res.ok) {
return res.json();
}
else {
console.log(res);
throw new Error(res.statusText);
}
})
.then(
(result) => {
this.setState({
images: result.items
});
}
).catch(error => console.log(error));
}
componentWillReceiveProps(nextprops) {
if (this.state !== nextprops.state) {
//same as componentDidMount
}
}
render() {
return (
<Gallery images={this.state.images} enableImageSelection={false} />
);
}
}
Given our conversation in the comments I can only assume that your search term is in a parent component. So what I recommend you to do is pass it to this component as a prop so you can do the following in your componentDid update:
componentDidUpdate(prevProps) {
const { searchTerm: previousSearch } = prevProps;
const { searchTerm } = this.props;
if (searchTerm !== previousSearch) fetch() ....
}
You can use getDerivedStateFromProps. It's the updated version of componentWillReceiveProps.
You should also read this, though: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
Using props to update internal state in a component can lead to complex bugs and there are often better solutions.

this keyword in reactJs

Hi I am confused about the 'this' keyword in ReactJS. I have the following code here for a simple counter in react.
import React, { Component } from "react";
import ReactDOM from "react-dom";
class ButtonClass extends React.Component {
constructor(props) {
super(props);
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.state = { count: 0 };
}
increment() {
this.setState({ count: this.state.count + 1 });
}
decrement() {
this.setState({ count: this.state.count - 1 });
}
render() {
const showCountStatus = this.state.count;
return (
<div>
<p>Click to increrment the button</p>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
<h1>{showCountStatus}</h1>
</div>
);
}
}
export default ButtonClass;
ReactDOM.render(<ButtonClass />, document.getElementById("root"));
The code works perfectly however, if I change the increment and decrement function to:
increment() {
this.setState({ count: count+ 1 });
}
decrement() {
this.setState({ count: count-1 });
}
Count is not defined error is shown
Any suggestions as to why? Thank you
As the error message says, there is no count variable defined.
From what I understood, it seems like you want to increment by one from previous state.
Then you can get a reference to the previous state and use that value to increment by one.
increment() {
this.setState(prevState => ({ count: prevState.count+ 1 }));
}
decrement() {
this.setState(prevState => ({ count: prevState.count-1 }));
}
Refer to State Updates May Be Asynchronous for more details on where prevState is from and how it is used.
The code which doesn't work, because count in count + 1, count - 1 is not from this.state object count key. JS is trying to find inside the function scope the variable definition, and it did not find it and throwing the correct error.
So to access the state object key count, you always need to write it like this.state.count.

Why can I not call upon the property of this object? (Angular, MongoDB)

I am making an angular application which uses a MongoDB database and NodeJS server.
The idea is that I make an application which for now only has a list of posts and beside that the detailed-post. The components are nicely standing next to eachother and working but I have one problem. When I try to retrieve a single post I can see via console.dir(post) that all is good and the object has been transmitted to the angular app. The problem is that when I try to use post.content I get an undefined message.
I have searched for hours but can not seem to find the cause of this. I would greatly appreciate any help you can give me. Beneath here is all the information, if you need to see something else, please tell me.
Thanks in advance.
This is the post-detail.component.html where I want to display the data.
<div class="row">
<div class="col-xs-12">
<p>Content:</p>
<h1>{{ post.content }}</h1>
</div>
</div>
The detail.ts file (I left out the imports)
#Component({
selector: 'app-post-detail',
templateUrl: './post-detail.component.html',
styleUrls: ['./post-detail.component.css']
})
export class PostDetailComponent implements OnInit {
post: Post = new Post();
id: string;
constructor(private postService: PostService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = params['id'];
this.postService.getPost(this.id).then(res => {
console.dir(res);
console.dir(res.content);
this.post = res;
});
}
);
}
}
The post.service.ts which I am using to retrieve the actual data:
#Injectable()
export class PostService {
postChanged = new Subject<Post[]>();
private headers = new Headers({'Content-Type': 'application/json'});
private serverUrl = environment.serverUrl + '/blogPosts/'; // URL to web api
private posts: Post[];
constructor(private http: Http) {
}
//this one DOES work
getPosts() {
console.log('Fetching BlogPosts from database.')
return this.http.get(this.serverUrl, {headers: this.headers})
.toPromise()
.then(response => {
this.posts = response.json() as Post[];
return response.json() as Post[];
})
.catch(error => {
return error;
});
}
getPost(index: string) {
console.log('Fetching individual BlogPost from database.');
console.log('index' + index);
if (index == null) {
console.log('null');
return null;
}
return this.http.get(this.serverUrl + index, {headers: this.headers})
.toPromise()
.then(response => {
console.dir(response.json().content);
console.dir(response.json());
return response.json() as Post;
})
.catch(error => {
return this.handleError(error);
});
}
}
The Post model:
export class Post {
private id: string;
private _content: string;
constructor(values: Object = {}) {
Object.assign(this, values);
}
public get _id(): string {
return this.id;
}
public set _id(n: string) {
this.id = n;
}
public get content(): string {
return this._content;
}
public set content(n: string) {
this._content = n;
}
}
And I added in the Postman GET /blogPost/id and the console log as images.
Thanks!
Console log
Postman GET route
I might be wrong but can you please change the _content to content everywhere in the service ?
Edit: are you sure the this.id is correct when you call the service method ? cause if it is null or undefined then return null will be executed.
Another note is that in Postman i see the response is an array of objects (one object) at this example. Can you try this.post = res[0]; in the component ?
return response.json() as Post;
In post.service.ts should be:
return response.json()[0] as Post;
I did not see that the object was wrapped in an array, by accessing it I was able to get it out and use it.

Resources