I'm brand spanking new to react. I am officially done beating my head against a wall. I just can't figure this out. This is my situation:
I am trying to get the results of an API call into a table. I have the call to the API working and results coming back. I am stuck on how to get my array updated with the data that came back. After that is done then I can populate the table with the data (at least logically that's the way what my brain tells me).
Initial form state setup:
const initialFormState = {
fileTypeId : '',
companyMasterId: '',
investmentCompanyId: '',
startDate: '',
endDate: '',
result: '',
fileLogs: []
}
All of the fields above are fields on the form\database. The API call takes these parameters to call a stored procedure that returns a result set based on the search parameters. fileLogs[] is where I want to put the data that comes back. I wasn't sure if I need to move it out of this setup and use useState just for that as a separate thing?
reducer initialization:
const [formState, dispatch] = useReducer (formReducer, initialFormState)
reducer setup
formReducer.js
import actionTypes from "./actionTypes"
const formReducer = (state, action) => {
switch (action.type) {
case actionTypes.handle_input_text:
return {
//using the spread operator (…state) to copy across all the properties and values from the state object.
//then we can modify the values of specific state properties by explicitly declaring them again with a new value.
...state,
[action.field]: action.payload,
}
case actionTypes.toggle_consent:
return{
...state,
hasConsented: !state.hasConsented
}
case actionTypes.on_success:
return{...state, filelogs: action.payload}
default:
return state
}
}
export default formReducer
API call
function getFileRouteLogs (e) {
e.preventDefault()
apiService.getFileRouteLogs(
formState.fileTypeId,
formState.companyMasterId,
formState.investmentCompanyId,
formState.startDate,
formState.endDate,
formState.result
)
.then((response)=>{
// dispatch({
// type: actionTypes.on_success,
// // payload: [...formState.fileLogs, response.data]
// payload: response.data
// })
formState.fileLogs = response.data
console.log(response.data)
})
Handler for form input changes
const handleInputChange = (e) => {
dispatch({
type: actionTypes.handle_input_text,
field: e.target.name,
payload: e.target.value
})
}
Form for input
return (
<div>
<h1>File Route Log Search</h1>
<hr />
<h2>Form field area</h2>
<Form onSubmit={getFileRouteLogs}>
<FormGroup row>
<Label for="fileTypeId" sm={2}>FileTypeId</Label>
<Col sm={2}>
<Input type="text" name="fileTypeId" value={formState.fileTypeId} onChange={(e) => handleInputChange(e)}></Input>
</Col>
</FormGroup>
<FormGroup row>
<Label for="companyMasterId" sm={2}>CompanyMasterId</Label>
<Col sm={2}>
<Input id="companyMasterId" type="text" name="companyMasterId" value={formState.companyMasterId} onChange={(e) => handleInputChange(e)} ></Input>
</Col>
</FormGroup>
...
Attempted table setup to hold data
const FileRouteLogTable = ({formState}) => {
return (
<table className="table">
<thead>
<tr>
<th>FileRouteLogId</th>
<th>CompanyMasterId</th>
<th>FileTypeId</th>
<th>Result</th>
<th>InvestmentCompanyMasterId</th>
<th>FileName</th>
</tr>
</thead>
<tbody>
{ (formState.length > 0) ? formState.map( (form, index) => {
return (
<tr key={ index }>
<td>{ form.fileRouteLogId }</td>
<td>{ form.companyMasterId }</td>
<td>{ form.fileTypeId}</td>
<td>{ form.result }</td>
<td>{ form.investmentCompanyMasterId }</td>
<td>{ form.fileName }</td>
</tr>
)
}) : <tr><td colSpan="5">Enter search parameters...</td></tr> }
</tbody>
</table>
);
}
export default FileRouteLogTable
I was going to try to use a react-table, but I got stuck on updating the data before I could do any table stuff
import { useTable } from 'react-table'
function FileRouteLogTable({ columns, formState }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
formState,
})
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
)
}
export default FileRouteLogTable
Any help would be greatly appreciated. All the examples I look at don't have my combination of factors:
UPDATE I fixed the casing issue that @johannchopin mentioned and changed the payload. I still didn't have any data in the table so I changed the table logic from:
{ (formState.length > 0) ? formState.map( (form, index) => {
to
{ (formState.fileLogs.length > 0) ? formState.fileLogs.map( (form, index) => {
and data is in the table
Put the fetchData function above in the useEffect hook and call it, like so: useEffect(() => { const url = "https://api.adviceslip.com/advice"; const fetchData = async () => { try { const response = await fetch(url); const json = await response. json(); console. log(json); } catch (error) { console.
AFAIK, useReducer hook does not offer callback functionality like useState does, which we could make API call in callback function.
useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
Sadly for you it seems to be a simple typo issue with fileLogs
. In the formReducer
function you assign action.payload
to filelogs
and not fileLogs
. After making this update you can again use the dispatch
function:
.then((response) => {
dispatch({
type: actionTypes.on_success,
payload: response.data
});
});
Checkout the live example:
The first thing you need to do is change the dispatch in the then in the api call.
dispatch({
type: actionTypes.on_success,
payload: [...formState.fileLogs, ...response.data]
})
What I did above is because I think your api is returning an array. If not so then the dispatch was ok. But right now the way you assigned the logs directly to the state, you can't do that. If your response contain all the data then you can dispatch like below
dispatch({
type: actionTypes.on_success,
payload: [...response.data]
})
As stated by user @johannchopin in the comments, the problem was caused by a typo in the reducer. it was "filelogs" which should have been "fileLogs". I have corrected that.
Change your formReducer.js to this:
const formReducer = (state, action) => {
switch (action.type) {
case actionTypes.handle_input_text:
return {...state, [action.field]: action.payload}
case actionTypes.toggle_consent:
return {...state, hasConsented: !state.hasConsented}
case actionTypes.on_success:
return {...state, fileLogs: action.payload}
default:
return state
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With