I am currently developing a custom component in ReactJS that is able to display a tree based on a JSON:
export default {
name: 'Main Node', nodeid: 99,toggled: true,
children: [
{ name: 'Secondary Node',nodeid: 0,
chosen: false,pushed: false,toggled: false,
children: [
{ name: 'Leaf Node',nodeid: 1,chosen: false,
pushed: false, toggled: false,
children: [{
name : "child 1", pushed: false,
toggled: false, chosen:false
}]
}
]
}
]
};
Currently, the component is able to render itself because I am sending the JSON from a static Data.js file as shown below:
import React from 'react';
import TreeComponent from 'reusable-components/TreeComponent';
import HierarchyJson from './internal/Data';
export default function ExampleTreeComponent() {
return <TreeComponent hierarchyJson={HierarchyJson} functionMode={4} htmlId="tree1"
pushLabelValue="include SubLevel"
unpushAlertText="plz choose something"
pushAlertText="plz choose something" />
}
What I want to achieve is to have the same functionality within my web pages, but receiving the JSON data from an HTTP call.
I am a beginner in ReactJS, so I'm lost at the moment.
Your tree component does not need to worry about where the data is coming from, rather it s only worried that there is data. In other words, whether you get the data from some local file or from an ajax request to an api should not be the concern of the tree component. All you need to to is make sure that the data that it requires gets sent down to it. Here is an example of what I mean.
class sample extends React.Component {
constructor() {
this.state = {
myData: {} //this is the tree
}
}
componentDidMount() {
ajax.request(some end point).then(data => {
this.setState({myData: data});
})
//this is where its recomended to make async calls. When the promise resolves
//the component will render again due to setState being called
//and this time myData will be your data from the api
//and now the tree component has what it needs to render the data
}
render() {
<Tree data={myData} /> //
}
}
Related
I am using react js. I want to make a chart using chartjs from the data I fetched from the api. Here, I just want to plot the chart between the below two arrays.
symbolsdateforchart: []
closingdateforchart: []
But, I am not sure how to use the value of these (please have a look at my code) since it has not been assigned yet, and the value get assigned in componentDidMount() and I making my chart inside this.state. Is there a better way to make the chart using chartjs between the above two array I mentioned. Or can I make this work somehow? Any help is appreciated.
My code:
import React from "react";
import { Line } from "react-chartjs-2";
import Chart from "./Chart";
export default class Symbols extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
symbolsdateforchart: [],
closingdateforchart: [],
search: "",
symbol: this.props.location.symbol,
//here I am making my chart
chartData: {
labels: ["how to add the data of symbolsdateforchart here"], //and here I know the data is not yet assigned to symbolsdateforchart: [], so not sure how to make it work
datasets: [
{
label: "Closing price",
data: ["how to add the data of closingdateforchart here"], //and here I know the data is not yet assigned to closingdateforchart: [], so not sure how to make it work
fill: false,
backgroundColor: [
"rgba(255,99,132,0.6)",
"rgba(54,162,235,0.6)",
"rgba(255,206,86,0.6)",
"rgba(75,192,192,0.6)",
"rgba(153,102,255,0.6)",
"rgba(255,159,64,0.6)",
"rgba(255,99,132,0.6)",
],
},
],
},
};
}
componentDidMount() {
let symbolsdate = [];
let closingprice = [];
fetch(`http://131.181.190.87:3001/history?symbol=${this.state.symbol}`)
.then((res) => res.json())
.then((json) => {
console.log(json);
for (const dataobj of json) {
let tempsymbolsDate = dataobj.timestamp.split("T")[0];
let tempclosingPrice = dataobj.close;
let tempArray = tempsymbolsDate.split("-");
tempsymbolsDate =
tempArray[2] + "/" + tempArray[1] + "/" + tempArray[0];
symbolsdate.push(tempsymbolsDate);
closingprice.push(tempclosingPrice);
}
this.setState({
isLoaded: true,
items: json,
//here the value get assigned for them
symbolsdateforchart: symbolsdate,
closingdateforchart: closingprice,
});
});
}
render() {
return (
//here I just print the table using the api I fetched in omponentDidMount()
);
}
}
In componentDidMount() you can update state variable chartData as
this.setState({
isLoaded: true,
items: json,
chartData: {
labels: symbolsdate,
datasets: [{...this.state.chartData.datasets, data: closingprice}]
}
});
Better is to separate dynamic part from static. Store static data like backgroundColor outside of state & provide statically in HTML of render() while, use state variables for dynamic data.
I'm using withRouter() to transform a dropdown menu in a router.
Unfortunately, I need to use hashed routes.
Is there a similar approach that can be used with HashRouter.
Thank you.
UPDATE:
This is the relevant source in main.js.
class App extends React.Component {
// ...
render() {
return (
React.createElement(ReactRouterDOM.BrowserRouter, null,
React.createElement("div", null,
// ...
React.createElement(ReactRouterDOM.withRouter(Dropdown), {
_value: this.state.foo,
_options: [this.state.foo],
_onChangeCallback: this.handleChangeCallback // this updates the state
}),
// ...
)
)
)
}
}
In dropdown.js the component changes the route accordingly with the dropdown:
export default class Dropdown extends React.Component {
// ...
componentWillMount() {
if (this.props._value) this.props.history.push(`/${this.props._value}`); // update route
}
handleChange(event) {
this.props._onChangeCallback(event.target.value); // update the state
}
// ...
render() {
const options = this.props._options.map((val) => React.createElement("option", {value: val}, val));
return React.createElement("div", null,
React.createElement("span", null,
React.createElement("select", {
onChange: this.handleChange,
value: this.props._value,
}, options)));
}
}
When the component mounts there is an HTTP request to fetch some data;
The fetched data is saved into the state and the dropdown menu updates;
When the option changes the route changes as well.
I'm fetching the data from an external API for food recipes, and getting the response in this format (JSON):
{
"count": 30,
"recipes": [
{
"publisher": "BBC Food",
"f2f_url": "http://food2fork.com/view/8c0314",
"title": "Chicken and cashew nut stir-fry",
"source_url": "http://www.bbc.co.uk/food/recipes/chickenandcashewnuts_89299",
"recipe_id": "8c0314",
"image_url": "http://static.food2fork.com/chickenandcashewnuts_89299_16x9986b.jpg",
"social_rank": 95.91061636245128,
"publisher_url": "http://www.bbc.co.uk/food"
},
{
"publisher": "Jamie Oliver",
"f2f_url": "http://food2fork.com/view/0beb06",
"title": "Roasted chicken breast with pancetta, leeks & thyme",
"source_url": "http://www.jamieoliver.com/recipes/chicken-recipes/roasted-chicken-breast-with-pancetta-leeks-and-thyme",
"recipe_id": "0beb06",
"image_url": "http://static.food2fork.com/466_1_1349094314_lrg2129.jpg",
"social_rank": 94.88568903341375,
"publisher_url": "http://www.jamieoliver.com"
},
{ ... more recipes ... }
]
}
And I'm trying to access that data and display, for the purpose of testing, just variables 'count', and the 'publisher' of the first recipe in array. This is my React code:
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { data: {} };
}
componentDidMount() {
fetch('https://www.food2fork.com/api/search?key=MY_KEY&q=chicken%20breast&page=2')
.then(response => {
return response.json();
})
.then(jsonData => {
this.setState({ data: jsonData }, function() {
console.log(jsonData);
});
});
}
render() {
return (
<div className="App">
<h1>{this.state.data.count}</h1>
<p>{this.state.data.recipes[0].publisher}</p> // Why this doesn't work?
</div>
);
}
};
If I remove the 'p' tag in the render() function, everything works as expected: the page loads at first, and then after fetching the data, displays '30' as 'h1'.
However, if I run the code with the 'p' tag, I get this error:
I'm searching for the answers for more than two hours and really can't find the answer. Why can I access the variable 'count', but not the variable 'publisher', which is inside of an array? I'm event logging out this.state after setting it, and object looks completely normal there. How can I access the elements in the 'recipes' array?
This is is because when you are fetching data at that time react render the component and you got error as this.state.data is still {} so this.state.data.recipes[0] is yet not defined as fetch request is not completed (it take some time). To resolve it you have to return on 2 conditions.
when fetch is running (Loading)
2) when fetch is completed
render() {
if(!this.state.data.recipes){
// if the fetch request is still not completed
return (
<div>
<h1>Loading .... </h1>
</div>
)
}
// run when fetch request is completed and this.state.data is now assigned some data
return (
<div className="App">
<h1>{this.state.data.count}</h1>
<p>{this.state.data.recipes[0].publisher}</p> // Why this doesn't work?
</div>
);
}
answer for your comment.
the error is can not read property 0 of undefined which means this.state.data.recipes is undefined and thus.state.data.recipes[0] is error right?.
But when you use this.state.data.count then you did not get error. As it will print undefined that is value of count (at that moment and you are not trying to print further like this.state.data.count.toString() if you do so it will through you error can not read property toString() of undefined).
and in then() when you use this.setState() it will update the state and react will re-render all affected components.
I have a list of items and I want to filter through them.
In order to do so I have a filtering component that looks like this:
[ text input "item name" ] [ dropdown "item category" ] [ date picker ]
I'm using React with Redux, react-redux and react-router. There's no "Filter" in my component, I want the items to get filtered as I type.
First of all I'm not sure how should I dynamically create the URL, so for example if you type "Abc" in the first input I want the current URL to be:
http://example.com/currentPage?filter=Abc&category=&dates=
As I type I want the URL to be changing constantly, so the URL actually builds as I type / choose options from dropdown / date picker. How could I achieve that with React Router?
Thanks
This is so straightforward. Just add onChange function to your input field. Like below.
import React from 'react';
class Filter extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
filter: '',
category: '',
dates: '',
}
}
handleChange(evt) {
this.setState({
filter: evt.target.value,
}, () => {
this.props.fetchFilteredItems(`http://example.com/currentPage?filter=${this.state.filter}&category=${this.state.category}&dates=${this.state.dates}`); // I assumed fetchFilteredItems comes from redux as an action.
});
}
render() {
return (
<div>
<input onChange={this.handleChange} />
<YourDropdown />
<YourDatePicker />
</div>
);
}
}
Hope this solves your problem.
This question regards collecting JSON-data from a local file and doing multiple state updates.
Im using import database from './db.json' to collect JSON-data in my parent component (app.js) is this a proper way to import local data?
db.json
{
"items": [
{
"key": 1,
"name": "Item 1",
},
{
"key": 2,
"name": "Item 2",
},
...
App.js
import React, { Component } from 'react';
import db from './db.json';
export default class App extends Component {
...
}
After import, database get set as a state
this.state = {
database: db,
term: '',
};
Example of a method
onChange = (e) => {
this.setState({term: e.target.value})
};
State and methods transfers as props to child components (stateless)
<Component onChange={this.onChange} stateOfDb={this.state.database} />
All my methods are placed in App.js and being passed down to stateless components, is this a good practice?
What if I want to create a search function and be able to filter thru each item in the database, how could a example of this look?
You can always filter stuff directly on render-time.
Ex:
updateSearch(ev){
this.setState({searchString: ev.target.value});
}
render(){
const filterData = this.state.searchString ? this.state.database.filter(row => row.someProperty.indexOf(this.state.searchString) > -1) : this.state.database;
<div>
<input type="text" onChange={(ev) => this.updateSearch(ev)} />
<ChildComponentToRenderData data={filteredData} />
</div>
}
You dont even have to put the database in state, since it is in fact just held in memory after the import. So in fact, instead of filtering using this.state.database, you can actually just do db.filter. Probably also nice to check if the searchString is actually set when filtering.