I have set up a call to fetch data from my firebase database using react native.
Database structure
Code inside FirebaseList.js
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentWillMount() {
firebase.database().ref('/signposts/items').on('value', snapshot => {
const dataArray = [];
const result = snapshot.val();
for (const data in result) {
dataArray.push(data);
}
this.setState({ data: dataArray });
console.log(this.state.data);
});
}
render() {
return (
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<Text>{item}</Text>
)}
keyExtractor={item => item}
/>
);
}
I believe the connection to firebase is successful as I can build and run the application. However, when the component renders, I do not see my two rows of data 'row1' and 'row2'.
You said your code is right then also check that this rules are set
{
"rules": {
"foo": {
".read": true,
".write": false
}
}
}
Note : - When you use the above rule your database is open for all Read more here. Make you use update the rules once you push to production.
If console.log(dataArray) shows an empty array (assuming that console.log() works...), try checking your connection:
componentDidMount() {
const ref = firebase.database().ref('/signposts');
const checkConnection = firebase.database().ref(`.info/connected`);
checkConnection.on('value', function(snapshot) {
if (snapshot.val() === true) { /* we're connected! */
firebase.database().ref('/signposts').on('value', snapshot => {
const dataArray = [];
const result = snapshot.val();
for (const data in result) {
dataArray.push(data);
}
if (dataArray.length === 0)
console.log("No data.")
else
this.setState({ listViewData: dataArray });
});
} else { /* we're disconnected! */
console.error("Check your internet connection.")
}
}
}
Related
Thanks for any support. I'm learning React and need to solve the problem consisting in that I can't make React to re-render after an item is deleted from a list.
Firstly I would like to say that I have follow the answers I found searching but still no luck.
The scenario is that I'm using React to fetch a list from and API and render it in the same screen with a form for editing and listing the specific information for every item in the list (fields are just name and lastname). The list is displayed with a button for edit which makes the form for edit, and with another button for delete. The list displays the two only fields which are name and lastname which are displayed using ListGroupItem from reacstrap that when onClick uses the form for listing only. I also have the logic for add items.
I'm able to add, update, list with no problems and re-rendering properly. However when deleting I'm just able to delete the item from the API but I have to manually re-render to display the update list
import React, { Component } from "react";
import { Button, Container, Row, Col } from "reactstrap";
import ListBebes from "./components/ListBebes";
import AddBebeForm from "./components/AddBebeForm";
import EditBebeForm from "./components/EditBebeForm";
import { fetchBebes, fetchBebe, addBebe, deleteBebe } from "./api";
import Websocket from "react-websocket";
class App extends Component {
constructor(props) {
super(props);
this.state = {
bebes: [],
bebe: {},
current_bebe_id: 0,
is_creating: true,
is_fetching: true,
is_justRead: true,
has_updated: false,
};
this.socket = React.createRef();
this.focusSocket = this.focusSocket.bind(this);
this.handleItemClick = this.handleItemClick.bind(this);
this.handleEditClick = this.handleEditClick.bind(this);
this.handleDeleteClick = this.handleDeleteClick.bind(this);
this.handleAddBebe = this.handleAddBebe.bind(this);
this.getData = this.getData.bind(this);
this.handleSaveBebe = this.handleSaveBebe.bind(this);
this.handleOnNombresChange = this.handleOnNombresChange.bind(this);
this.handleOnApellidosChange = this.handleOnApellidosChange.bind(this);
}
componentDidMount() {
this.getData();
}
componentDidUpdate(prevProps, prevState) {
if (this.state.has_updated === true) {
this.getData();
this.setState({ has_updated: false });
}
}
focusSocket() {
this.socket.current.focus();
}
async getData() {
let data = await fetchBebes();
this.setState({ bebes: data, is_fetching: false });
}
async handleItemClick(id) {
let selected_bebe = await fetchBebe(id);
this.setState((prevState) => {
return {
is_creating: false,
is_justRead: true,
current_bebe_id: id,
bebe: selected_bebe,
};
});
}
async handleEditClick(id) {
let selected_bebe = await fetchBebe(id);
this.setState((prevState) => {
return {
is_creating: false,
is_justRead: false,
current_bebe_id: id,
bebe: selected_bebe,
};
});
}
async handleDeleteClick(id) {
let antesBebes = [...this.state.bebes];
console.log(antesBebes);
let index = antesBebes.findIndex((i) => i.id === id);
console.log(`the index es ${index} y el id es ${id}`);
await deleteBebe(id);
antesBebes.splice(index, 1);
console.log(antesBebes);
this.setState({ bebes: [...antesBebes], has_updated: true });
//this.setState({ bebes: this.state.bebes, has_updated: true });
//console.log(antesBebes);
console.log("it was deleted...");
//window.location.reload();
//this.setState((prevState) => {
//return {
//bebes: antesBebes,
//has_updated: true,
//};
//});
//this.getData();
}
handleAddBebe() {
this.setState((prevState) => {
return { is_creating: true };
});
}
async handleSaveBebe(data) {
await addBebe(data);
await this.getData();
}
handleData(data) {
let result = JSON.parse(data);
let current_bebe = this.state.bebe;
if (current_bebe.id === result.id) {
this.setState({ bebe: result });
}
}
handleOnNombresChange(e) {
let nombres = e.target.value;
let current_bebe = this.state.bebe;
current_bebe.nombres = nombres;
this.setState({
bebe: current_bebe,
has_updated: true,
});
const socket = this.socket.current;
socket.state.ws.send(JSON.stringify(current_bebe));
}
handleOnApellidosChange(e) {
let apellidos = e.target.value;
let current_bebe = this.state.bebe;
current_bebe.apellidos = apellidos;
this.setState({
bebe: current_bebe,
has_updated: true,
});
//const socket = this.refs.socket;
const socket = this.socket.current;
socket.state.ws.send(JSON.stringify(current_bebe));
}
render() {
return (
<>
<Container>
<Row>
<Col xs="10">
<h2>Hello</h2>
</Col>
<Col>
<Button color="primary" onClick={this.handleAddBebe}>
Create a new note
</Button>
</Col>
</Row>
<Row>
<Col xs="4">
{this.state.is_fetching ? (
"Loading..."
) : (
<ListBebes
bebes={this.state.bebes}
handleItemClick={(id) => this.handleItemClick(id)}
handleEditClick={(id) => this.handleEditClick(id)}
handleDeleteClick={(id) => this.handleDeleteClick(id)}
></ListBebes>
)}
</Col>
<Col xs="8">
{this.state.is_creating ? (
<AddBebeForm handleSave={this.handleSaveBebe} />
) : (
<EditBebeForm
handleNombresChange={this.handleOnNombresChange}
handleApellidosChange={this.handleOnApellidosChange}
bebe={this.state.bebe}
soloLeer={this.state.is_justRead}
/>
)}
<Websocket
ref={this.socket}
url="ws://127.0.0.1:8000/ws/bebes"
onMessage={this.handleData.bind(this)}
/>
</Col>
</Row>
</Container>
</>
);
}
}
export default App;
Can you debug the following lines? and print [...antesBebes] | this.state.bebes and antesBebes after line 3 ?
I have some suspension about these lines, Can't debug them though because you haven't added all your components in here.
1 antesBebes.splice(index, 1);
2 console.log(antesBebes);
3 this.setState({ bebes: [...antesBebes], has_updated: true });
My recommendation is to use one of the following to manage your state in react application:
React Hooks -- recommended for small application like yours link
React Redux -- link
I found the solution. It happened that by placing code in the delete function handleDeleteClick() and also in componentDidUpdate I was messing things up.
The final code for delete is:
async handleDeleteClick(id) {
let antesBebes = [...this.state.bebes];
let index = antesBebes.findIndex((i) => i.id === id);
await deleteBebe(id);
antesBebes.splice(index, 1);
await this.setState({ bebes: antesBebes });
}
This code may have other problems but as far as the original goal was, this solve the problem.
I'm successfully fetching an array of objects from a local ethereum blockchain (successful as in, I've logged the data in componentDidMount and it is what its supposed to be). Its a dynamically sized array
(inventory), so I gotta fetch it element by element. Here are the relevant parts:
async componentDidMount() {
this.setState({fetching: true}, () => {
this.loadData()
.then(result => this.setState({fetching: false}))
})
}
async loadData() {
if (typeof window.ethereum !== 'undefined') {
const web3 = new Web3(window.ethereum)
const netId = await web3.eth.net.getId()
const accounts = await web3.eth.getAccounts()
this.setState({account: accounts[0]})
if(typeof accounts[0] !== 'undefined'){
const balance = await web3.eth.getBalance(accounts[0])
this.setState({balance: balance})
}
else {
window.alert('Please login with MetaMask')
}
try {
const plotRepository = new web3.eth.Contract(PlotRepository.abi, PlotRepository.networks[netId].address)
const plot = await plotRepository.methods.claimPlot(this.state.account).send({from: this.state.account})
this.setState({plot: new web3.eth.Contract(Plot.abi, _plot.plot)})
const size = await this.state.plot.methods.size().call()
const tiles = await this.state.plot.methods.getTiles().call()
await this.loadInventory() // this is where I'm loading the array in question
this.setState({size: size})
this.setState({tiles: tiles})
// logging the array here works
} catch (e) {
window.alert(e)
}
} else {
window.alert('Please install MetaMask')
}
}
async loadInventory() {
for (const [key, value] of Object.entries(inventoryinfo)) {
const item = await this.state.plot.methods.getInventoryItem(key).call()
this.setState({inventory: [...this.state.inventory, {
name: key,
value: parseInt(item[0][1]),
count: item[1]
}]})
}
}
Again, logging the array in the fetching functions works just fine. I'm also using a flag (fetching) which denotes whether all data has been successfully loaded or not, and I only try to render data once everything is loaded:
render() {
const { plot, account, fetching, tiles, inventory } = this.state
return (
<div className="App">
<p>Currently logged in with account: { this.state.account }</p>
<p>Balance: { this.state.balance } </p>
{ fetching ? // make sure things are fetched before rendering them
<p>Loading</p> :
<div>
<UserPlot tileData={tiles} plot={plot} account={account}/>
{
inventory[0].name // inventory[0] is undefined
// inventory.length is not
}
</div>
}
</div>
);
}
Trying to display any element of the array gives me an undefined error. Rendering the array length works though ({inventory.length}). This is weird since I'm doing the same thing with another array that I'm fetching and displaying in the UserPlot component and that works just fine (only difference is that in this case the array is static and I can load it all in one go but I don't think that has anything to do with it)
I am trying to add sorting to my movie app, I had a code that was working fine but there was too much code repetition, I would like to take a different approach and keep my code DRY. Anyways, I am confused as on which method should I set the state when I make my AJAX call and update it with a click event.
This is a module to get the data that I need for my app.
export const moviesData = {
popular_movies: [],
top_movies: [],
theaters_movies: []
};
export const queries = {
popular:
"https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=###&page=",
top_rated:
"https://api.themoviedb.org/3/movie/top_rated?api_key=###&page=",
theaters:
"https://api.themoviedb.org/3/movie/now_playing?api_key=###&page="
};
export const key = "68f7e49d39fd0c0a1dd9bd094d9a8c75";
export function getData(arr, str) {
for (let i = 1; i < 11; i++) {
moviesData[arr].push(str + i);
}
}
The stateful component:
class App extends Component {
state = {
movies = [],
sortMovies: "popular_movies",
query: queries.popular,
sortValue: "Popularity"
}
}
// Here I am making the http request, documentation says
// this is a good place to load data from an end point
async componentDidMount() {
const { sortMovies, query } = this.state;
getData(sortMovies, query);
const data = await Promise.all(
moviesData[sortMovies].map(async movie => await axios.get(movie))
);
const movies = [].concat.apply([], data.map(movie => movie.data.results));
this.setState({ movies });
}
In my app I have a dropdown menu where you can sort movies by popularity, rating, etc. I have a method that when I select one of the options from the dropwdown, I update some of the states properties:
handleSortValue = value => {
let { sortMovies, query } = this.state;
if (value === "Top Rated") {
sortMovies = "top_movies";
query = queries.top_rated;
} else if (value === "Now Playing") {
sortMovies = "theaters_movies";
query = queries.theaters;
} else {
sortMovies = "popular_movies";
query = queries.popular;
}
this.setState({ sortMovies, query, sortValue: value });
};
Now, this method works and it is changing the properties in the state, but my components are not re-rendering. I still see the movies sorted by popularity since that is the original setup in the state (sortMovies), nothing is updating.
I know this is happening because I set the state of movies in the componentDidMount method, but I need data to be Initialized by default, so I don't know where else I should do this if not in this method.
I hope that I made myself clear of what I am trying to do here, if not please ask, I'm stuck here and any help is greatly appreciated. Thanks in advance.
The best lifecycle method for fetching data is componentDidMount(). According to React docs:
Where in the component lifecycle should I make an AJAX call?
You should populate data with AJAX calls in the componentDidMount() lifecycle method. This is so you can use setState() to update your component when the data is retrieved.
Example code from the docs:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
fetch("https://api.example.com/items")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.items
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item.name}>
{item.name} {item.price}
</li>
))}
</ul>
);
}
}
}
Bonus: setState() inside componentDidMount() is considered an anti-pattern. Only use this pattern when fetching data/measuring DOM nodes.
Further reading:
HashNode discussion
StackOverflow question
I am trying to upload image one by one with change and delete option(for each image uploaded) in multiple view with react, apollo client. But with this I can't get the clear thought about how to perform this easily and confused a lot..
Please anyone help me to get rid of this...
**updated**
Hi now i am using react-dropzonecomponent so far, but here i did mutiple file upload with delete option only..
Here i can send the files to server(node using mulitpart form data), in DB create the file in server end and store the path in database with path name only... But here i can't show the image files in front end from the path got from Back end...
const initialState = {
files: [],
imagePreviewUrl: []
};
class Image extends React.Component {
constructor(props) {
super(props);
this.state = { ...initialState };
}
componentWillMount() {
let {match, data} = this.props;
const id = match.params.id && match.params.id.slice(1);
if (id) {
let currentProduct = (data && data.getProduct) && data.getProduct.find((data) => {
return data.id == id;
});
this.setState({
imagePreviewUrl: currentProduct.images
});
}
}
handleAdd(file) {
console.log(file)
var allFiles = this.state.files;
allFiles = allFiles.concat([file]);
this.setState({
files: allFiles
});
}
handleRemove(file) {
let allFiles = this.state.files;
this.state.files.forEach((itr, i) => {
if (itr.upload.uuid == file.upload.uuid) {
allFiles.splice(i, 1)
}
});
this.setState({
files: allFiles
});
console.log(this.state.files, allFiles, file)
}
render() {
let {match, classes, data} = this.props;
let {imagePreviewUrl} = this.state;
const id = match.params.id && match.params.id.slice(1);
var self = this;
return (
<GridContainer>
<DropzoneComponent
config={{
postUrl: 'no-url',
iconFiletypes: ['.jpg', '.png', '.gif'],
showFiletypeIcon: true
}}
eventHandlers=
{{
addedfile: (file) => this.handleAdd(file),
removedfile: (file) => this.handleRemove(file),
init: (dropzone) => {
console.log(dropzone)
}
}}
djsConfig={{
autoProcessQueue: false,
addRemoveLinks: true,
previewTemplate: ReactDOMServer.renderToStaticMarkup(
...<img data-dz-thumbnail="true" /> ...)}} />
</GridContainer>
);
}
}
export default withStyles(style)(Image);
I have a table of ships, and am trying to implement sorting (using table header clicks) and filtering (using a text field that the user types in).
I am puzzled by how React handles the state of my component.
My understanding is that componentDidUpdate() works like this:
I make a change to the component state somewhere
The state change is detected by the component and componentDidUpdate() runs
Based on this understanding, I expected componentDidUpdate() to
Re-sort when I change the state of ships
Re-filter when I change the state of ships
However, when a sorting is triggered, filtering is not done.
I thought that this would happen:
State is changed, triggering componentDidUpdate()
Ships are sorted
The state is saved
The saving of the state triggers a re-run of componentDidUpdate()
this.state.ships is now different from prevState.ships, triggering a re-filtering
But this seems to happen:
State is changed, triggering componentDidUpdate()
Ships are sorted
The state is saved
The saving of the state triggers a re-run of componentDidUpdate()
this.state.ships is the same as prevState.ships, not triggering a re-filtering
So either my understanding of componentDidUpdate() is spotty, or my understanding of state synchronicity is. I have read that state can be asynchronous in event handlers. Perhaps the sorted ships are not yet saved into the state when I try to detect if I should be filtering?
import React, { Component } from 'react';
import { SearchBar } from '../SearchBar';
import { Table } from '../Table/Table';
import { MoreButton } from '../MoreButton/MoreButton';
export class SearchableSortableTable extends Component {
constructor(props) {
super(props);
this.fetchShips = this.fetchShips.bind(this);
this.filterShips = this.filterShips.bind(this);
this.setSearchExpression = this.setSearchExpression.bind(this);
this.setSort = this.setSort.bind(this);
this.state = {
ships: [],
filteredShips: [],
searchExpression: '',
reverseSort: false
};
}
render() {
return (
this.state.error ?
<div>
<div>There was a problem fetching the ships, sorry.</div>
<div>{this.state.error}</div>
</div>
:
this.state.ships.length === 0 ? <h4>Loading...</h4> :
<div>
<div>
<SearchBar setSearchExpression={this.setSearchExpression} />
<MoreButton className="di" url={this.state.nextUrl} fetchShips={this.fetchShips} />
</div>
<div>
<Table ships={this.state.filteredShips} setSort={this.setSort} sortBy={this.state.columnName} reverse={this.state.reverseSort} />
</div>
</div>
);
}
componentDidMount() {
this.fetchShips(this.props.url);
}
componentDidUpdate(prevProps, prevState) {
if (this.state.columnName !== prevState.columnName || this.state.reverseSort !== prevState.reverseSort) {
this.sortShips();
}
// This conditional block is not entered when I sort.
if (this.state.ships !== prevState.ships || this.state.searchExpression !== prevState.searchExpression) {
this.filterShips();
}
}
async fetchShips(url) {
try {
const response = await fetch(url);
if (response['status'] && response['status'] === 200) {
const json = await response.json();
const ships = json['results'].map(this.mapShip);
this.setState({
ships: this.state.ships.concat(ships),
nextUrl: json['next']
});
} else {
this.setState({ error: `${response['status']} ${response['statusText']}` });
}
} catch (error) {
if (error instanceof TypeError && error.message.includes('NetworkError')) {
this.setState({ error: `${error.name} ${error.message}` });
} else {
throw error;
}
}
}
filterShips() {
const filteredShips = this.state.ships.filter(ship => {
return Object.values(ship).some(shipProp => shipProp.includes(this.state['searchExpression']))
});
this.setState({
filteredShips: filteredShips
});
}
setSearchExpression(event) {
this.setState({ searchExpression: event.target.value });
}
setSort(event) {
if (event && event['currentTarget'] && event['currentTarget']['attributes'] &&
event['currentTarget']['attributes']['name'] && event['currentTarget']['attributes']['name']['nodeValue']) {
const columnName = event['currentTarget']['attributes']['name']['nodeValue'];
this.setState({
columnName,
reverseSort: columnName === this.state.columnName ? !this.state.reverseSort : false
});
}
}
sortShips() {
if (this.state.columnName) {
const sortedShips = this.state.ships.sort((a, b) => {
const propA = a[this.state.columnName];
const propB = b[this.state.columnName];
if (!isNaN(+propA)) {
return this.state.reverseSort ? Number(propB) - Number(propA) : Number(propA) - Number(propB);
}
return this.state.reverseSort ? propB.localeCompare(propA) : propA.localeCompare(propB);
});
this.setState({ ships: sortedShips });
}
}
/**
* Maps a ship to its name, manufacturer, cost and starship class.
* #param ship The ship to be mapped.
*/
mapShip(ship) {
const { name, manufacturer, cost_in_credits, starship_class } = ship;
return Object.assign(
{
name,
manufacturer,
cost_in_credits,
starship_class
},
{}
);
}
}
The shouldComponentUpdate() method works for both props and state. In your example, after the sort/filter events, the following method is fired by React. Try using,
shouldComponentUpdate(nextProps, nextState) {
return this.state.value != nextState.value;
}