React.js function component useState related questions - javascript

I want to access state data.
How do I copy an array object I have into a data array?
-code
function TableList() {
const [state, setState] = React.useState({
columns: [
{ title: '로트번호', field: 'lote_id' },
{ title: '중량(kg)', field: 'item_weight' },
{ title: '수량', field: 'item_quantity' },
{ title: '제품코드', field: 'item_code' },
{ title: '제품명', field: 'item_name' },
{ title: '규격', field: 'standard' },
{ title: '재질', field: 'material' },
{ title: '공정명', field: 'process_name' },
{ title: '단중', field: 'unitweight' },
{ title: '납기일시', field: 'duedate' },
{ title: '단가', field: 'item_cost' },
{ title: '금액', field: 'item_price' },
],
data: [],
});
useEffect(() =>{
console.log("실행")
console.log(state.data);
axios.get('http://localhost:8080/api/item/1')
.then( response => {
//I want to copy the data of my output.list to state.data.
})
.catch( response => {
console.log(response);
})
});
I want to copy my axios response data into the data array declared as useState.
Please tell me the most effective way.
How do I access the second argument, state.data?
I want to access and put my output.list response data.

Not totally clear on what you're looking for, but if you just want to update the data prop on your state object, you can do it like this:
const arrayOfObjects = [{ id: 1 }, { id: 2 }];
const newState = Object.assign({}, state);
newState.data = arrayOfObjects;
setState(newState);

This is not a great way to use useState.
const [columns,setColumns] = React.useState([bla,bla]);
const [data, setData] = React.useState([bla, bla]);
You should declare your state variables separately like above.

useEffect(() =>{
console.log("실행")
console.log(state.data);
axios.get('http://localhost:8080/api/item/1')
.then( response => {
//console.log(response);
var output = response && response.data;
const newState = Object.assign({}, state);
newState.data = output.list;
setState(newState);
})
.catch( response => {
console.log(response);
})
});
I want to call it just once like the mounted function

Related

Trying to export data to csv file from my firebase data gives undefined

I have a list of data from my firebase firestore that I want to export to .csv
I did everything that is required but when I add the values that I want to be exported they are always undefined.
I am not an expert in react I am somewhat intermediate but I think it is because I am setting my data inside a useEffect Hook.
My data useState is undefined, although it holds values and I can see them in my table, which is causing the CSVLink to throw errors.
How do I allow my data to be passed into the headers?
Here is my code:
const [data, setData] = useState([]);
const [id, setID] = useState("");
const list = []
const filteredList = []
useEffect(() => {
firebase.firestore().collection("Users").get().then((userSnapshot) => {
userSnapshot.forEach((doc) => {
const {powerAccount,first_name,registerDate,email,company,country,phone} = doc.data();
setID(doc.data().usersID)
list.push({
usersID:doc.id,
powerAccount:powerAccount,
first_name:first_name,
registerDate:registerDate,
email:email,
company:company,
country:country,
phone:phone,
});
});
setData(list);
});
},[]);
const headers = [
// here all the keys give undefined.
{label:'User',key:data.usersID},
{label:'Account',key:data.powerAccount},
{label:'Name',key:data.first_name},
{label:'RegistrationDate',key:data.registerDate},
{label:'Email',key:data.email},
{label:'Company',key:data.company},
{label:'Country',key:data.country},
{label:'Phone',key:data.phone},
];
const csvReport = {
filename:"userReport.csv",
headers:headers,
data: data // also my data useState is undefined, although it holds values and i can see them in my table
}
return (
<CSVLink {...csvReport} >
Export
</CSVLink>
)
According to your implementation, fetching data from firebase is async so the csvData is getting undefined because it's not updating after a state update
Try changing your code like this and let me know if it works fine
const [data, setData] = useState({
filename: "userReport.csv",
headers: [],
data: [],
});
const [id, setID] = useState("");
const filteredList = [];
useEffect(() => {
firebase
.firestore()
.collection("Users")
.get()
.then((userSnapshot) => {
let list = [];
userSnapshot.forEach((doc) => {
const {
powerAccount,
first_name,
registerDate,
email,
company,
country,
phone,
} = doc.data();
setID(doc.data().usersID);
list.push({
usersID: doc.id,
powerAccount: powerAccount,
first_name: first_name,
registerDate: registerDate,
email: email,
company: company,
country: country,
phone: phone,
});
});
const headers = [
// I'm not sure why you need this key
// but if it's only for uniqueness
// you can replace them by unique strings like
// { label: "User", key: "user" },
// { label: "Account", key: "account" },
{ label: "User", key: data.usersID },
{ label: "Account", key: data.powerAccount },
{ label: "Name", key: data.first_name },
{ label: "RegistrationDate", key: data.registerDate },
{ label: "Email", key: data.email },
{ label: "Company", key: data.company },
{ label: "Country", key: data.country },
{ label: "Phone", key: data.phone },
];
const csvReport = {
filename: "userReport.csv",
headers: headers,
data: list,
};
setData(csvReport);
});
}, []);
return <CSVLink {...data}>Export</CSVLink>;
You should all state coordination / update to useState and useEffect hooks and avoid relying on any field update outside the scope of these.
You should then remove the list variable, move state update to your effect hook and consolidate all users data in the same structure:
const [data, setData] = useState([]);
useEffect(() => {
firebase.firestore()
.collection("Users")
.get()
.then((userSnapshot) => {
const usersData = [];
userSnapshot.forEach((doc) => {
const { powerAccount, first_name, registerDate, email, company, country, phone, userID } = doc.data();
const userData = {
usersID: doc.id,
powerAccount: powerAccount,
first_name: first_name,
registerDate: registerDate,
email: email,
company: company,
country: country,
phone: phone,
};
const headers = [
// here all the keys give undefined.
{ label: 'User', key: userID },
{ label: 'Account', key: powerAccount },
{ label: 'Name', key: first_name },
{ label: 'RegistrationDate', key: registerDate },
{ label: 'Email', key: email },
{ label: 'Company', key: company },
{ label: 'Country', key: country },
{ label: 'Phone', key: phone },
];
const csvReport = {
filename: "userReport.csv",
headers: headers,
data: userData
}
usersData.push(csvReport);
});
setData(usersData);
});
}, []);
return (
<CSVLink {...data} >
Export
</CSVLink>
)
You may need add loading state to reflect the UI effect of data being loaded.
I think there are two things that causes the problem that you need to understand.
Asynchronous Function
React Lifecycle
Fetching data from firebase is asynchronous and might take sometime before you get the returned data while you have saved csvReport as constant variables and set it up as React element properties. So when firebase is still loading your data and your react component is already rendered / mounted, your data state has value of [] from default value as defined in the useState statement. Based on your code, your csvReport constant variable will not be receiving new data from firebase unless your app is re-rendered (enter new lifecycle and repeat). For example, switching to other tab component and go back to this component without refreshing the browser.
const csvReport = {
filename:"userReport.csv",
headers:headers, => [{ label: "User", key: undefined }, ...etc]; undefined bcs `data` is []
data: data => the value is []
}
So the simple solution is NOT to save the data as constant variable and set up the React element properties directly from your useState variable. Based on your code, I would make some changes like this.
...your previous code
const getHeaders = () => {
// Do your data manipulation using `data` in useState
// For example:
const headers = data && data.map(item => {return {id: item.id}})
return headers
}
return (
<CSVLink
filename="userReport.csv"
headers={getHeaders()}
data={data}
>
Export
</CSVLink>
)
Hope this helps and have fun making changes :)

How can I use fetched const value in react?

const fetchStocks = () => {
const API_KEY = "dddd";
const StockSymbol = 'IBM';
const API_Call = `https://www.alphavantage.co/query?function=OVERVIEW&symbol=${StockSymbol}&apikey=${API_KEY}`;
const StockSymbols = [];
const StockName = [];
const StockIndustry = [];
fetch(API_Call)
.then(
function (response) {
return response.json();
}
)
.then(
function (data) {
console.log(data);
StockSymbols.push(data['Symbol']);
StockName.push(data['Name']);
StockIndustry.push(data['Industry']);
console.log(StockName)
console.log(StockSymbols)
console.log(StockIndustry)
})
// const table = {
// columns: [
// { headername: "Symbol", field: "symbol" },
// { headername: "Name", field: "name" },
// { headername: "Industry", field: "industry" }
// ],
// rowData: [
// { symbol: StockSymbols, name: StockName, industry: StockIndustry }
// ]
// }
return (
<div>
{StockSymbols}, {StockName}, {StockIndustry}
</div>
)
};
export default fetchStocks;
I'm trying to get stock name, symbol, and industry using Alpha Vantage.
And when I console.log them, it seems like it's working.
But when I try to use them in the return part with {StockSymbols}, {StockName}, {StockIndustry},
nothing passes and nothing shows up.
What do I have to do to use them?
Hope this is what you are looking for, as your question is not self explanatory enough.
Create a component state
const [data, setData] = useState({StockName: null, StockSymbols: null, StockIndustry: null})
Move fetch(API_Call) in useEffect hook. (componentDidMount incase not using hooks)
useEffect(() => {
fetch(API_Call)
.then(
function (response) {
return response.json();
}
)
.then(
function (data) {
setData({
StockName: data['Symbol'],
StockSymbols: data['Name'],
StockIndustry: data['Industry']
})
})
}, [])
Use component state in JSX
return (<>
<p>{data.StockName}</p>
<p>{data.StockSymbols}</p>
<p>{data.StockIndustry}</p>
</>
If you're aiming to:
fetch the data on mount of the React component
then show it in the UI
you'd need to keep some state that re-renders the component. So for you that probably means initiating the array as const [StockIndustry, setStockIndustry] = useState([]); and setStockIndustry([...StockIndustry, data['Symbol']]; in your life cycle method/hook. Simply pushing to the array will not re-render the component.

useEffect hook called on initial render without dependency changing

Okay, I am experiencing some behaviour I don't really understand.
I have this useState hook
const [permanent, setPermanent] = useState(false)
and this useEffect hook
useEffect(() => {
if (permanent) {
dispatch({ value: 'Permanent booth', key: 'period' })
} else {
dispatch({ value: '0', key: 'period' })
}
}, [permanent])
It triggers a rerender on initial render, and I do not call setPermanent upon rendering my component, I have checked this both by commenting every single setPermanent call out in my application. And I have also tried replacing it with a function that logs to the console.
//const [permanent, setPermanent] = useState(false)
const permanent = false
const setPermanent = () => {
console.log('I am called') //does not get called on initial render
}
I know it triggers a rerender because when I comment one of the second dispatch call in it out, it does not trigger the rerender.
useEffect(() => {
if (permanent) {
dispatch({ value: 'Permanent booth', key: 'period' })
} else {
//dispatch({ value: '0', key: 'period' })
}
}, [permanent])
Is there a reason for this, because I cannot seem to find documentation explaining this behaviour?
EDIT --------------
const shopOptions = (() => {
const options = [
{ label: 'Choose a shop', value: '0' },
]
Object.keys(stores).forEach(store => {
options[options.length] = { label: store, value: options.length }
})
return options
})()
const genderOptions = [
{ label: 'Choose a gender', value: '0' },
{ label: 'Female', value: '1' },
{ label: 'Male', value: '2' }
]
const periodOptions = [
{ label: 'Choose a period', value: '0' },
{ label: '1 week', value: '1' },
{ label: '2 weeks', value: '2' },
{ label: '3 weeks', value: '3' },
{ label: '4 weeks', value: '4' }
]
const initialState = {
shop: shopOptions[0],
gender: genderOptions[0],
period: periodOptions[0],
}
function reducer(prevState, { value, key }) {
const updatedElement = { ...prevState[key] }
updatedElement.value = value
return { ...prevState, [key]: updatedElement }
}
//form
const [state, dispatch] = useReducer(reducer, initialState)
useEffect hooks run both after the first render and after every update of variables passed to the dependency array (in your case [permanent]).
Because you have a boolean value that triggers the effect, it's hard to know whether it's the first render or a re-render within the effect. In your case I would consider not using a useEffect here, and instead dispatching what you need while updating the state. For example:
const [permanent, setPermanent] = useState(false)
const makePermanent = () => {
setPermanent(true)
dispatch({ value: 'Permanent booth', key: 'period' })
}
const makeTemporary = () => {
setPermanent(false)
dispatch({ value: '0', key: 'period' })
}
This is my solution. Just a boolean property and ordering hook will do the job.
const _isMounted = React.useRef(false)
const [filter, setFilter] = React.useState('')
React.useEffect(() => {
if (_isMounted.current) {
getData(filter) // Now it will not get called very first time
}
}, [filter])
/* Order is important. [] or so called componentDidMount() will be the last */
React.useEffect(() => {
_isMounted.current = true
return () => {
_isMounted.current = false
}
}, [])

MBDReact: How do I make a <datatable> row clickable?

I have setup my table in admin side of our application with MDBReact using the datatable. This table shows some small details of the stories that I have.
Now I have to make a row clickable i.e. add onClick to make a function call with the story id passed as an argument to this function.
Question:
How do I add onClick event to the datatable row?
(Below is my code.)
class Posts extends Component {
componentDidMount() {
this.getPosts();
}
getPosts = async () => {
const response = await fetch("http://****************/get_posts");
const post_items = await response.json();
this.setState({ posts: post_items.result }, () => {
console.log(this.state.posts);
this.setState({ tableRows: this.assemblePost() });
});
};
assemblePost = () => {
let posts = this.state.posts.map((post) => {
let mongoDate = post.dateAdded.toString();
let mainDate = JSON.stringify(new Date(mongoDate));
return {
postTitle: post.storyTitle,
// postDescription: post.storyDescription,
dateAdded: mainDate.slice(1, 11),
thankedBy: post.thankedBy.length,
reportedBy: post.reportedBy ? post.reportedBy.length : "",
userEmail: post.userEmail[0],
categoryName: post.categoryName[0],
};
});
console.log(posts);
return posts;
};
state = {
posts: [],
tableRows: [],
};
render() {
const data = {
columns: [
{
label: "Story Title",
field: "postTitle",
},
{ label: "Category Name", field: "categoryName" },
{
label: "User Email",
field: "userEmail",
},
{
label: "Date Added",
field: "dateAdded",
},
{
label: "Thanked",
field: "thankedBy",
},
{
label: "Reported",
field: "reportedBy",
},
],
rows: this.state.tableRows,
};
return (
<div className="MDBtable">
<p className="posts">Posts List</p>
<MDBDataTable striped bordered hover data={data} />
</div>
);
}
}
export default Posts;
To pull this off, here's what I did, but you'll need to appreciate these:
MDBDataTable requires you to manually define the columns and rows.
For data to render seamlessly, you define columns.field that correspond to rows[key]
Now, here's the logic, if you define a rows[key] that does not correspond to any columns.field, then that rows[key] is defined for an entire row just like we often pass index when working with map().
So based on the above observations,you can just pass the click event as a key/value pair to the row.And it will work just fine.
// ...
assemblePost = () => {
let posts = this.state.posts.map(
(post, i) => {
let mongoDate = post.dateAdded.toString();
let mainDate = JSON.stringify(new Date(mongoDate));
return {
index: i + 1, // advisable to pass a unique identifier per item/row
clickEvent: () => this.handleClick(storyId), // pass it a callback function
postTitle: post.storyTitle,
// ...others
categoryName: post.categoryName[0],
};
});
console.log(posts);
return posts;
};
// ...
Notice this clickEvent: () => this.handleClick(storyId), will be attached to the entire row.

Update object value in array React hooks way

How would I update a title of the specific id with hooks state setup. Here:
const NotesContainer = ({
}) => {
const [notesDummyData, setNotesDummyData] = useState([
{
id: '5',
title: 'Sauna',
},
{
id: '7',
title: 'Finland',
},
]);
const onChangeItemName = (newTitle, oldTitle, itemId) => {
//Update a new title here for id 7
};
Could not find any example for setState hooks.
Just map through the items and if the id is equal to the selected id you modify only the value:
const onChangeItemName = (newTitle, oldTitle, itemId) => {
setNotesDummyData(notesDummyData.map(x => {
if(x.id !== itemId) return x
return {...x, title: newTitle}
}))
}
You can use Array.map(). For each item check if the id is equal to itemId. If it this, spread the item, and replace the title. If not, return the original item:
const onChangeItemName = (title, itemId) => {
setNotesDummyData(notesDummyData.map(o => o.id === itemId ? ({
...o,
title
}) : o));
};
It's easy to update data using React Hook, but there is not working setState(), so there will be working [notThis, thisOne(setNotesDummyDate)] to update your data.
const [notesDummyData, setNotesDummyData] = useState([
{
id: '5',
title: 'Sauna',
},
{
id: '7',
title: 'Finland',
},
]);
Using React Hook method to Update data:
const onChangeItemName = (newTitle, oldTitle, itemId) => {
setNotesDummyDate = useState([
{
id: itemId, // Update
title: newTitle,
},
{
id: itemId, // Update
title: newTitle,
},
]);
}
Still Curious, study here about useState()
Cheer you!

Categories

Resources