My problem is that Excel Export is exporting data to Excel before fetching is completed.
What I want to achieve: Fetch data when the button is clicked, then after all the data is fetched, export the data to excel. NB I do not want to fetch data when the component is rendered for the first time.
This is only one of my attempts:
class ExcelExport extends React.Component {
getDataForExport = () => {
get('/uri')
.then((response) => response.items)
.catch((error) => []);
};
render() {
return (
<Workbook element={<input type="button" value="Excel Export" />}>
<Workbook.Sheet data={() => this.getDataForExport()} name="Sheet A">
<Workbook.Column label="Foo" value="foo"/>
</Workbook.Sheet>
</Workbook>
);
}
}
Thank you for any help.
One possible solution to this problem would be to use the component state to store your data. So lets assume the call to get in your getDataForExport function returns data when the fetch is completed (the promise is resolved). In the then() call you could assign that data to the state like so:
class ExcelExport extends React.Component {
constructor() {
super();
this.state = { data: null }
}
getDataForExport = () => {
get('/uri')
.then((response) => {
this.setState({ data: response });
})
.catch((error) => []);
};
...
}
Then in your render function, you could add a button that triggers the data fetching and then you check whether this.state.data is null or whether some data is included and render different stuff depending on that:
render() {
return (
<Workbook element={<input type="button" value="Excel Export" onClick={this.getDataForExport.bind(this)} />}>
{this.state.data ||
<Workbook.Sheet data={this.state.data} name="Sheet A">
<Workbook.Column label="Foo" value="foo"/>
</Workbook.Sheet>}
</Workbook>
);
}
Hope this helps.
Related
I want to add data and see in below, and also when I start app, I want see added records. But I can see it, when I'm try to writing something in the fields.
The thing is, the function that updates the static list is asynchronous. This function retrieves data from the database, but before assigning it to a variable, the page has been rendered. There is some way to wait for this variable or update information other way than when you try to type it in the fields. This is before the form is approved.
[![enter image description here][1]][1]
class AddAdvertisment extends React.Component <any, any> {
private advertisment;
constructor(props, state:IAdvertisment){
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
this.state = state;
this.advertisment = new Advertisement(props);
}
onButtonClick(){
this.advertisment.add(this.getAmount(), this.state.name, this.state.description, this.state.date);
this.setState(state => ({ showRecords: true }));
}
updateName(evt){
this.setState(state => ({ name: evt.target.value }));
}
....
render() {
return (<React.Fragment>
<div className={styles.form}>
<section className={styles.section}>
<input id="name" onChange={this.updateName.bind(this)} ></input>
<input id="description" onChange={this.updateDescription.bind(this)} ></input>
<input type="date" id="date" onChange={this.updateDate.bind(this)} ></input>
<button className={styles.action_button} onClick={this.onButtonClick.bind(this)}>Add</button>
</section>
</div>
{<ShowAdvertismentList/>}
</React.Fragment>
);
}
class ShowAdvertismentList extends React.Component <any, any>{
render(){
let listItems;
let array = Advertisement.ad
if(array !== undefined){
listItems = array.map((item) =>
<React.Fragment>
<div className={styles.record}>
<p key={item.id+"a"} >Advertisment name is: {item.name}</p>
<p key={item.id+"b"} >Description: {item.description}</p>
<p key={item.id+"c"} >Date: {item.date}</p>
</div>
</React.Fragment>
);
}
return <div className={styles.adv_show}>{listItems}</div>;
class Advertisement extends React.Component {
public static ad:[IAdvertisment];
constructor(props){
super(props);
if(!Advertisement.ad){
this.select_from_db();
}
}
....
select_from_db = async () => {
const res = await fetch('http://localhost:8000/select');
const odp = await res.json();
if(odp !== "brak danych")
odp.forEach(element => {
if(Advertisement.ad){
Advertisement.ad.push(element);
}
else{
Advertisement.ad = [element];
I try to create function and child like:
function Select_from_db(){
const[items, setItems] = useState();
useEffect(() => {
fetch('http://localhost:8000/select')
.then(res => res.json())
.then(data => setItems(data));
}, []);
return <div className={styles.adv_show}>{items && <Child items={items}/>}
</div>;
}
function Child({items}){
return(
<>
{items.map(item => ( ...
))}
</>
And is working good in first moment, but if I want add item to db I must refresh page to see it on a list below.
I use is instead ShowAdvertismentList in render function. Elements be added to db but not showing below. In next click is this same, until refresh page.
And in my opinio better use a list, becouse I musn't want to conect to database every time to download all records.
[1]: https://i.stack.imgur.com/IYSNU.gif
I now recipe. I must change state on componentDidMount in AddAdvertisment class.
async componentDidMount(){
let z = await setTimeout(() => {
this.setState(state => ({ loaded: true}));
}, 1000);
}
render() {
return (<React.Fragment >
(...)
{this.state.loaded ? <ShowAdvertismentList /> : <Loading/>}
</React.Fragment>
);
}
So I was trying to implement states in Child component from the Parent component state,as you can see in the code.But it gives me undefined as state value in child componenet.To test you can conosle.log(questions) and you will see undefined.
Is there a mechanism to setState in Parent component in some way such that the subsequent props in child components wil be able to get the state values?
Here is my code:
import React, { useEffect, useState } from "react";
import io from "socket.io-client";
const ENDPOINT = "http://localhost:5000";
let socket = io(ENDPOINT);
export default function Screen() {
const [qValue, setQuestion] = useState({personalInfo:{},questions:[]});
const [aValue, setAnswer] = useState({personalInfo:{},answer:""});
useEffect(() => {
socket.on("screenAns", (input) => {
setAnswer(JSON.parse(input));
});
console.log(aValue);
}, [aValue]);
useEffect(() => {
socket.on("screenQs", (arrayValue) => {
setQuestion(JSON.parse(arrayValue));
});
console.log((qValue));
}, [qValue]);
return (
<div>
<h2>Screen</h2>
<QuestionSingleMode value={qValue} />
</div>
);
}
function QuestionSingleMode(props){
var [questions,setQuestions]=useState(props.value.questions);
var [renderQuestion,setRenderQuestion]=useState("")
var [counter,setCounter]=useState(props.value.questions.length)
useEffect(()=>{
console.log(questions)
setRenderQuestion(questions[0])
},[renderQuestion])
function nextQuestion(){
setQuestions(questions.splice(0,1))
setRenderQuestion(questions[0])
setCounter(counter--)
}
return(
<div>
<h1>{renderQuestion}</h1>
<button onClick={nextQuestion}>{counter ? "next" : "finish"}</button>
</div>
)
}
Actually I solved the issue by changing the renderQuestion to props.questions in the useEffect() array.
Hello I am working on a process with React that will allow users to select a row or rows from a table by selecting check-boxes.
I need assistance with how once a row is checked, how can I store this information but at the same time if the row is unchecked I would also want to update the state.
Than when the user selects the submit button it will send the array object to the server side.
I have an empty array in my state and in the method that handles selecting a checkbox I am attempting to push the data to the array and than send the array with a form.
It appears as if the array is not being updated or I am missing something?
class TestStatus extends Component {
constructor (props) {
super(props)
this.state = {
selected: []
}
handleCheckChildeElement = (event) => {
var data = this.global.data;
data.forEach(data => {
if(data.testid === event.target.value) {
data.isChecked = event.target.checked
if(event.target.checked === true) {
this.setState({ selected: [ ...this.state.selected, data]
});
}
console.log(this.state.selected);
}
});
this.setGlobal({ data });
}
handleSubmit(event) {
event.preventDefault();
axios.post('http://localhost:5000/api/advanced_cleanup',
this.state.selected)
.then((res) => {
console.log("Sending tests");
}).catch(event => console.log(event));
}
render() {
return(
<div>
<table>
<AdvancedRows checked={this.handleCheckChildeElement}
handleCheckChildeElement={this.handleCheckChildeElement}/>
</table>
<form className="ui form" onSubmit={this.handleSubmit}>
<button
className="ui basic blue button" type="submit"
style={{ marginBottom: '5em' }}>
Submit
</button>
</form>
</div>
);
}
}
I expect to be able to select a checkbox or multiple and update the state array based on what is checked and than send that data to the server side.
After some additional research online I found the correct way with react to update the state array and than update it upon unchecking a check box.
If the targeted row is checked it will pass that rows object into the state array otherwise if the check box of the row is unchecked it will iterate over the state array and filter out the item that was unchecked.
This is the guide I used to assist me. https://scriptverse.academy/tutorials/reactjs-update-array-state.html
if(event.target.checked === true) {
this.setState({ selected: [...this.state.selected, data ] });
} else {
let remove = this.state.selected.map(function(item) {
return item.testid}).indexOf(event.target.value);
this.setState({ selected: this.state.selected.filter((_, i) => i !== remove) }); }
Expanding on my comment above.
handleCheckChildeElement = (event) => {
var data = this.global.data;
// create an empty array so that each click will clean/update your state
var checkedData = [];
data.forEach(data => {
if(data.testid === event.target.value) {
data.isChecked = event.target.checked
if(event.target.checked === true) {
// instead of setting your state here, push to your array
checkedData.push(data);
}
console.log(checkedData);
}
});
// setState with updated checked values
this.setState({selected: checkedData});
this.setGlobal({ data });
}
So I have a component that shows categories from firestore, the component shows nothing the first time but when I click navbar button again it does show the data stored in firestore.
Here is the component file :
import * as React from "react";
import Category from "./Category";
import connect from "react-redux/es/connect/connect";
import {getCategories} from "../reducers/actions/categoryAction";
class CategoriesList extends React.Component{
constructor(props) {
super(props);
this.state = ({
categoriesList: [{}]
})
}
componentWillMount() {
this.props.getCategories();
this.setState({categoriesList: this.props.categories});
this.forceUpdate();
}
render() {
return (
<div className={'container categories'}>
<div className={'row center'} onClick={() => this.props.history.push('/addcategories')}>
<div className={'col s24 m12'}>
<p>Create New Category</p>
</div>
</div>
<div className={'row'}>
<div className={'col s24 m12'}>
{/*{() => this.renderCategories()}*/}
{this.state.categoriesList && this.state.categoriesList.map(category => {
return <Category category={category} key={category.id}/>
})}
</div>
</div>
</div>
);
}
}
const mapDisptachToProps = (dispatch) => {
return {
getCategories: () => dispatch(getCategories()),
}
};
const mapStateToProps = (state) => {
return {
categories: state.category.categories
}
};
export default connect(mapStateToProps, mapDisptachToProps)(CategoriesList)
And here is the reducer file:
import db from '../firebaseConfig'
const initState = {
categories: []
};
const categoryReducer = (state=initState, action) => {
switch (action.type) {
case 'CREATE_CATEGORY':
db.collection("Categories").add({
category: action.category.name
})
.then(function(docRef) {
db.collection("Categories").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// console.log(`${doc.id} => ${doc.data().category}`);
if(doc.id === docRef.id) {
state.categories.push({id: doc.id, name: doc.data().category});
console.log(state.categories)
}
});
});
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
break;
case 'GET_CATEGORIES':
console.log('Getting data from firestore');
db.collection("Categories").get().then((querySnapshot) => {
if(state.categories.length !== querySnapshot.size) {
querySnapshot.forEach((doc) => {
state.categories.push({id: doc.id, name: doc.data().category});
});
}
});
break;
}
return state;
};
export default categoryReducer
Is there any way to update the component after fully loading the data? or a way to load all the data in the initalState?
There are few things one needs to understand. First, this.props.getCategories() performs an action that is asynchronous in nature and hence in the very next line this.setState({categoriesList: this.props.categories});, we wont get the required data.
Second, Storing props to state without any modification is un-necessary and leads to complications. So try to use the props directly without storing it. In case you are modifying the obtained props, make sure you override getDerivedStateFromProps apropiately.
Third, Try to use componentDidMount to perform such async operations than componentWillMount. Refer when to use componentWillMount instead of componentDidMount.
Fourth(important in your case), Reducer should not contain async operations. Reducer should be a synchronous operation which will return a new state. In your case, you need to fetch the data elsewhere and then dispatch within your db.collection(..).then callback. You can also use redux-thunk, if you are using too many async operations to get your redux updated.
So #Mis94 answer should work if you follow the fourth point of returning the new state in the reducer rather than mutating the redux directly in the db().then callback
First, you don't need to store the component's props in the state object. This is actually considered an anti-pattern in react. Instead of doing this, just use your props directly in your render method:
render() {
return (
<div className={'container categories'}>
<div className={'row center'} onClick={() => this.props.history.push('/addcategories')}>
<div className={'col s24 m12'}>
<p>Create New Category</p>
</div>
</div>
<div className={'row'}>
<div className={'col s24 m12'}>
{/*{() => this.renderCategories()}*/}
{this.props.categories && this.props.categories.map(category => {
return <Category category={category} key={category.id}/>
})}
</div>
</div>
</div>
);
}
Hence in your componentWillMount you only need to initiate your request:
componentWillMount() {
this.props.getCategories();
}
You can also do it in componentDidMount() lifecycle method.
Now when your request resolves and your categories update in the store (Redux) they will be passed again to your component causing it to update. This will also happen with every update in the categories stored in the store.
Also you don't have to call forceUpdate like this unless you have components implementing shouldComponentUpdate lifecycle method and you want them to ignore it and do a force update. You can Read about all these lifecycle methods (and you have to if you are using React) here.
I am building a web client (react,redux) & API (mongo, express, node) that will show a list of deals to a user and allow them to "favorite/like" them. I am new to react/redux, as you will be able to tell. I am using axios to make my requests and have successfully rendered a list of deals. I have a "favorite" button that successfully makes the post request, and the request just sends back the deal that was favorited.. However, the "number of likes" is not updating and does not show the increased number until I manually refresh the page.
Here is my component that successfully produces a list of deals (2)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchDeals, favoriteDeal } from '../actions';
import DealCard from './DealCard';
class DealList extends Component {
componentDidMount(){
this.props.fetchDeals();
this.favoriteDeal = this.favoriteDeal.bind(this);
}
favoriteDeal = (dealId) => {
this.props.favoriteDeal(dealId)
}
renderDeals(){
return this.props.deals.map(deal => {
return(
<DealCard
onFavorite = {this.favoriteDeal}
key={deal._id}
{...deal}
/>
)
});
}
render(){
return(
<div>
{this.renderDeals()}
</div>
);
}
}
function mapStateToProps(state){
return {
deals: state.deals,
favoriteDeal: state.favoritedDeal
}
}
export default connect(mapStateToProps, {fetchDeals, favoriteDeal})(DealList)
Below is my individual deal card:
import React, { Component } from 'react';
class DealCard extends Component {
render() {
return (
<div key={this.props._id} className="card" style={{width: "18rem", marginTop: 10}}>
<img className="card-img-top" src={this.props.dealImage} style={{maxHeight: 200}} alt="${this.props.dealHeadline}" />
<div className="card-body">
<h4>{this.props.dealHeadline}</h4>
<p className="card-text">{this.props.dealDescription}</p>
<div>
<button onClick={() => this.props.onFavorite(this.props._id)}>Favorite</button>
<span>{this.props.dealId}</span>
<i className="fa fa-heart" aria-hidden="true"></i>
<p className="card-text">#of Likes: {this.props.dealNumberOfLikes}</p>
</div>
</div>
</div>
);
}
}
export default DealCard;
Below are my action creators:
export const fetchDeals = () => async dispatch => {
const res = await axios.get('/api/deals')
dispatch({type: FETCH_DEALS, payload: res.data})
};
export const favoriteDeal = (dealId) => async dispatch => {
const res = await axios.post(`/api/deals/${dealId}/favorites`)
dispatch({type: FAVORITE_DEAL, payload: res.data})
};
and finally my reducers:
// deals reducer
import { FETCH_DEALS } from '../actions/types';
export default function (state = [], action){
switch(action.type){
case FETCH_DEALS:
return action.payload;
default:
return state;
}
};
// favorite deals Reducer
import { FAVORITE_DEAL } from '../actions/types';
export default function (state = {}, action){
switch(action.type){
case FAVORITE_DEAL:
return action.payload;
default:
return state;
}
};
To summarize: I have a list of deals, and each deal has a button that when clicked, "favorites" a deal via an HTTP post request and increases the NumberOfDealLikes by 1. When the button is clicked, the request is successfully executed and the database shows that the NumberOfDealLikes is increased by one. However, on the screen, the update is not shown until I manually rerender. As twitter works, I would like to show that the increase happens simultaneously.
Thank you all for your help!
I think the problems lies in your favorite_deal reducer. As you said, the post request sends back the updated deal. It should then replace the old one in the deals array. Your deals reducer should look like:
import { FETCH_DEALS, FAVORITE_DEAL } from '../actions/types';
export default function (state = [], action){
switch(action.type){
case FETCH_DEALS:
return action.payload;
case FAVORITE_DEAL:
return state.map((d) => d._id === action.payload._id ? action.payload : d);
default:
return state;
}
};
As the deals array is updated, your component will be re-rendered. And you do not need another reducer.
By the way, as you defined the favoriteDeal function as a class property with an arrow function, you do not need to bind it to this.