I'm trying to display location coordinates (of vehicles) on a map with data that I'm fetching every 3 seconds. Every time I fetch the data (array of objects with attribute "Longitude" and "Latitude"), the state will update and I want to update the "markers" on a map to reflect the vehicles' latest positions.
I know I'm fetching the data but the markers are not showing up. Is there something wrong with the way I loop?
class Mapbox extends Component {
constructor(props){
super(props)
this.state = {
active_vehicles: {},
};
}
componentDidMount() {
this.interval = setInterval(() => this.fetchData(), 3000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
fetchData = async () => {
let url = `${request_url}`
const response = await fetch(url, {
method: "GET",
headers: {
"Accept": "application/json",
}
});
const body = await response.json()
this.setState({ active_vehicles: body })
}
createMarkers = () => {
let markers = []
if(this.state.active_vehicles){
for (let i = 0; i < this.state.active_vehicles.length; i++) {
markers.push(
<Marker latitude={this.state.active_vehicles[i]["Latitude"]} longitude={this.state.active_vehicles[i]["Longitude"]}>
<div>x</div>
</Marker>
)
}
return markers
}
}
render() {
return (
<ReactMapGL
// mapbox API access token
mapboxApiAccessToken={MAPBOX_TOKEN}
mapStyle="mapbox://styles/mapbox/dark-v9"
{...this.state.viewport}
onViewportChange={(viewport) => this.setState({viewport})}>
<div>
{this.createMarkers()}
</div>
</ReactMapGL>
);
}
}
Correct this.active_vehicles to this.state.active_vehicles (OP has corrected after I posted my comment)
Add key attribute to the Marker component inside the for loop: <Maker key={i} ...
Related
I am new to react.js so troubles caught me. I have small todo-list app connected with mockAPI. Application gets todo list data from API. As required, I call API inside componentDidMount() instead of constructor. However, API is called twice (only after page reloaded, not data manipulation as put\delete data to API). Any errors or warnings in console.
class App extends Component {
todoServ = new TodoServer();
constructor(props) {
super(props);
this.state = { data: [], maxId: 0 };
}
/*
code to add\delete\done todo item;
*/
findCurrentMaxId = (data) => {
const idList = [];
data.forEach(todo => {
idList.push(todo.id);
});
return Math.max(...idList);
}
updateTodoData = (data) => {
const maxId = this.findCurrentMaxId(data);
this.setState({ data, maxId });
}
getTodoData = () => {
this.todoServ
.getTodoList()
.then(this.updateTodoData)
.catch(this.errorTodoData);
}
componentDidMount() {
this.getTodoData();
}
render() {
return (
<div className="app">
<div className="content">
<AddTodoListItem onAddNewTodoItemData={this.onAddNewTodoItemData}/>
<TodoList
data={this.state.data}
onDoneTodoItemData={this.onDoneTodoItemData}
onDeleteTodoItemData={this.onDeleteTodoItemData} />
</div>
</div>
)
}
}
export default App;
Console:
This is the service fetches data.
class TodoService {
#url = `https://*secret*/todoslist/todo`;
async getResource(url) {
let res = await fetch(url);
if (!res.ok) {
throw new Error(`Could not fetch ${url}, status: ${res.status}`);
}
return await res.json();
}
async getTodoList() {
const res = await this.getResource(this.#url);
console.log('GET', res);
return res;
}
}
export default TodoService;
Thanks for the advices.
I want to change a API parameter by click function and render new data. When I trigger componentDidUpdate by onclick event listener,the api data changed first and worked fine for first click. But When click second time the api call ran completely. The parameter currentPage is assigned to this.state.count and this this.state.count valued in incremented on click.
My code below:
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [],
count: 1,
};
}
componentDidMount() {
this.ProductList();
}
componentDidUpdate() {
let change = document.getElementById("change");
change.addEventListener("click",(e)=>{
this.changeParams();
this.ProductList();
})
}
changeParams = (e) =>{
this.setState({count: this.state.count + 1})
}
ProductList() {
var myHeaders = new Headers();
myHeaders.append("Cookie", "PHPSESSID=822cu5ctftcpo8f98ehklem4k9");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch("http://192.168.31.236/magento/rest/V1/products?searchCriteria[filterGroups][0][filters][0][field]=category_id& searchCriteria[filterGroups][0][filters][0][value]=2& searchCriteria[filterGroups][0][filters][0][conditionType]=eq&searchCriteria[sortOrders][0][field]=price& searchCriteria[sortOrders][0][direction]=ASC& searchCriteria[pageSize]=20& searchCriteria[currentPage]="+this.state.count, requestOptions)
.then(response => response.text())
.then(result => this.setState({products:result}),)
.catch(error => console.log('error', error));
}
render() {
const productsList = () =>{
let pro = [];
if(typeof this.state.products === 'string') {
pro = JSON.parse(this.state.products)
console.log(pro)
}else{
pro = []
}
if(pro.items && typeof pro.items !== "undefined"){
return pro.items.map((item, i) => (
<div>
<h1>{ item.name }</h1>
</div>
));
}
}
return(
<div>
{productsList()}
<button id="change">Change</button>
</div>
);
}
}
export default App;
Rather than manually attaching event listeners, do it through React. In pretty much most cases you shouldn't be doing DOM operations directly.
class App extends React.Component {
// ...
/* You don't need this
componentDidUpdate() {
}
*/
handleChangeClick = () => {
this.changeParams();
this.ProductList();
}
// ...
render() {
// ...
return(
<div>
{productsList()}
<button id="change" onClick={this.handleChangeClick}>Change</button>
</div>
);
}
}
The reason why your approach doesn't work is because React may be producing and destroying DOM elements in ways you don't expect, so making sure you manually attach and detach event listeners to the right elements is difficult to get right.
I'm trying to dynamically generate a table from a fetch request. It's able to do it with JSON data without an array name, however when it does, it doesn't work. Here is the code: https://codesandbox.io/s/static-example-319q4
Here, the example works fine with the data that doesn't have an array name for the JSON data, however, when the other componentDidMount function is used, it doesn't work even though I specified the array name using "posts.launches".
class App extends React.Component {
constructor(props){
super(props);
this.state = {
posts: [],
value: '',
}
}
/*
Get response from an API endpoint and populates the
*/
componentDidMount() {
//const params = this.state.text
const url = "https://jsonplaceholder.typicode.com/posts";
fetch(url, {
method: "GET"
})
.then(response => response.json())
.then(posts => {
this.setState({ posts: posts });
});
}
/*
componentDidMount() {
//const params = this.state.text
const url = "https://hn.algolia.com/api/v1/search?query=redux";
fetch(url, {
method: "GET"
})
.then(response => response.json())
.then(posts => {
this.setState({ posts: posts.hits });
});
}
*/
getColumns() {
const getPostKeys = this.state.posts[0];
if (getPostKeys) {
const column =
this.state.posts &&
Object.keys(getPostKeys).map(key => {
return {
Header: key,
accessor: key
};
});
return column;
} else {
console.log("Error")
return [];
}
}
render() {
console.log(this.state.posts[0])
const columns = this.getColumns();
// console.log(JSON.stringify(this.state.initial_data));
return (
<div>
<ReactTable
data={this.state.posts}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
filterable
/>
<br />
</div>
);
}
}
ReactDOM.render( <
App / > ,
document.getElementById('app')
);
Any help would be great! Thanks!
Some of the data in your JSON is not consistent with the input that React Table expects its to be in a grid. Check for the condition in working example -
"_tags" && x !== "_highlightResult"
After removing these keys, I further baked the columns and its working fine. Please check the working example -
https://codesandbox.io/s/static-example-x2kjr
Code -
getColumns() {
const getPostKeys = this.state.posts[0];
if (getPostKeys) {
function isNotTagsOrHighlightKey(x) {
return x !== "_tags" && x !== "_highlightResult";
}
const getSanitizedColumns = Object.keys(getPostKeys).filter(
isNotTagsOrHighlightKey
);
const newColumn = getSanitizedColumns.map(key => {
return {
Header: key,
accessor: key
};
});
return newColumn;
} else {
console.log("Error");
return [];
}
}
I'm trying to make a search functionality for my app. It works with API:
http://localhost:3005/products?q=[USER INPUT HERE]
and .JSON is returned from this. I already have a working component that I want to duplicate and use it for search results display. It looks like this:
class Item extends Component {
constructor(props) {
super(props);
this.state = {
output: {},
url: {}
}
}
componentDidMount() {
fetch(this.props.url)
.then(response => response.json())
.then(data => this.setState({ output: data }));
}
render() {
const { general = {name:"", description:""} } = this.state.output;
return (
<BoxTitle>{general.name}</BoxTitle>
);
}
}
working alright, rendered this way:
let ChoosePage = (i) => {
ReactDOM.unmountComponentAtNode(document.getElementById('items'))
let urls = [
'http://localhost:3005/products/774944',
'http://localhost:3005/products/774945',
...
'http://localhost:3005/products/738471'];
let urls_sliced = urls;
if (i === 0) {
urls_sliced = urls.slice(0, 4);
} else if (i === 1) {
urls_sliced = urls.slice(4, 8);
} else if (i === 2) {
urls_sliced = urls.slice(-2);
}
let show_items = () => {
ReactDOM.render(urls_sliced.map((url)=>{
return(
<Item url={url}/>
)
}), document.getElementById('items'));
}
show_items()}
this is my input field:
const search_box = (
<form>
<Icon>search</Icon>
<input placeholder={'Search...'}></input>
</form>
);
I'm looking for a way to pass value inputted by the user to function that will convert it to link and use for getting .JSON from API and then render components mapped with this data. Managed to make only this:
let url_s = 'http://localhost:3005/products?q=' + input;
let show_results = () => {
ReactDOM.render(urls_sliced.map((url)=>{
return(
<Item url={url_s}/>
)
}), document.getElementById('items'));
}
show_results()
Help is very appreciated here :)
The startUpload method inside <Items /> will call the callback function to update the state of the parent component each time it receives a response, This causes <Items /> to be rendered unnecessarily multiple times.
My expected effect is that after the state is updated, only the <Results /> component needs to be re-rendered
class Parent extends React.Component {
constructor(props) {
super(props);
this.getResponseData = this.getResponseData.bind(this);
this.state = {
responseData: [],
}
}
getResponseData(data) {
this.setState({
responseData: this.state.responseData.concat(data),
})
}
render() {
return (
<div>
<Items files={this.props.files} updateData={this.getResponseData}/>
<Results data={this.state.responseData}/>
</div>
)
}
}
class Items extends React.Component {
componentDidMount() {
this.startUpload(this.props.files)
}
startUpload(files) {
const URL = 'http://localhost:3000/upload';
for (let i = 0, len = files.length; i < len; i++) {
const data = new FormData();
data.append('img', files[i]);
fetch(URL, {
method: 'post',
body: data,
})
.then(checkStatus)
.then(parseJSON)
.then(data => {
this.props.updateData(data);
})
}
}
render() {
const filesData = this.getFilesData(this.props.files);
let imageItems = filesData.map((current) => {
return (
<div>
<img src={current.objectURL} alt="preview"/>
</div>
)
});
return <div>{imageItems}</div>;
}
}
function Results(props) {
const responseData = props.data;
let result = [];
if (responseData.length) {
result = responseData.map(current => {
return <p>{current}</p>
});
return <div>{result}</div>
}
}
https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate You can use shouldComponentUpdate to inform your component whether or not should re-render or not based on a change in state/props. Using this knowledge, you can implement the logic you need in order to render the Items/Results component only when needed.
Hope that helps!