React Admin: how to fix endless loading in Create - react-admin

I'm stuck in endless loading when Create some post "contents" with REST API
there is no error, 200 ok response, and success to POST
but still in loading status, after receive 200 ok
so, I can't chack error massage just loading...
when I go to list manually, there is created new post
every thing working in backend, but loading is not stop..
I try to use onSuccess, but onSuccess does not start
I guess submission is not end..
help me!
edit : Here is more code! I'm still stuck in loading..
other post(ex.Themes) is created successfully! but contents is not..!
this is my dataprovider
getOne: async (resource, params) => {
if (resource === "themes" || resource === "curriculums") {
const { json } = await httpClient(`${PROXY}/${resource}/${params.id}`);
return {
data: json,
};
} else {
const { json: json_1 } = await httpClient(
`${PROXY}/${resource}/${params.id}`
);
return {
data: {
id: json_1.id,
title: json_1.title,
category: json_1.category,
type: json_1.type,
description: json_1.description,
thumbnail: JSON.parse(json_1.metadata).thumbnail,
files: JSON.parse(json_1.metadata).files,
createdDate: json_1.createdDate,
modifiedDate: json_1.modifiedDate,
},
};
}
},
const getFormData = (object) => {
let formData = new FormData();
Object.getOwnPropertyNames(object).forEach((key) => {
if (key === "files") {
let fileData = object[key];
let i,
j = 0;
for (i = 0, j = fileData.length; i < j; i += 1) {
formData.append(key, fileData[i]);
}
} else {
formData.append(key, object[key]);
}
});
return formData;
};
create: async (resource, params) => {
if (resource !== "contents") {
// console.log(params);
const json = await httpClient(`${PROXY}/${resource}`, {
method: "POST",
body: JSON.stringify(params.data),
});
return {
data: { ...params.data, id: json.body },
};
} else {
const formedData = getFormData(params.data);
// for (var pair of formedData.entries()) {
// console.log(pair[0] + ", " + pair[1]);
// }
const json_1 = await httpClient2(`${PROXY}/${resource}`, {
method: "POST",
body: formedData,
});
return {
data: { ...params.data, id: json_1.body },
};
}
},
this is extanding dataprovider
create: (resource, params) => {
if (resource !== "contents" || !params.data.metadata.rawFile) {
// fallback to the default implementation
return dataProvider.create(resource, params);
} else {
const newPictures = params.data.metadata.rawFile;
let transFiles = [];
if (params.data.files) {
const newFiles = params.data.files.filter(
(p) => p.rawFile instanceof File
);
newFiles.map((f) => (transFiles = transFiles.concat(f.rawFile)));
}
// console.log(transFiles);
return new Promise(() => {
dataProvider.create(resource, {
...params,
data: {
title: params.data.title,
type: params.data.type,
category: params.data.category,
description: params.data.description,
thumbnail: newPictures,
files: transFiles,
},
});
});
}
},
};
this is Posts.js code
export const PostShow = (props) => (
<Show title={<PostTitle />} {...props}>
<SimpleShowLayout>
<TextField source="id" />
<TextField source="title" />
<TextField source="category" />
<TextField source="type" />
<TextField source="description" />
<ImageField source="thumbnail">
<FunctionField
label="Image"
render={(record) => {
return <img src={record.thumbnail} alt="thumbnail" />;
}}
/>
</ImageField>
<FilesField source="files" />
<DateField label="Publication date" source="createdDate" />
<DateField label="Modified date" source="modifiedDate" />
</SimpleShowLayout>
</Show>
);
export const PostEdit = (props) => {
return (
<Edit {...props}>
<SimpleForm>
<TextInput disabled source="id" />
<TextInput source="title" />
<TextInput source="category" />
<TextInput source="type" />
<TextInput multiline source="description" />
<ImageInput
source="metadata"
label="thumbnail"
accept="image/*"
placeholder={<p>Drop your thumbnail here</p>}>
<ImageField source="src" />
</ImageInput>
<FileInput source="files" label="Related files" multiple={true}>
<FileField source="src" title="title" />
</FileInput>
</SimpleForm>
</Edit>
);
};
export const PostCreate = (props) => {
return (
<Create {...props}>
<SimpleForm redirect="list">
<TextInput source="title" />
<TextInput source="category" />
<TextInput source="type" />
<TextInput multiline source="description" />
<ImageInput
source="metadata"
label="thumbnail"
accept="image/*"
placeholder={<p>Drop your thumbnail here</p>}
validate={required()}>
<ImageField source="src" />
</ImageInput>
<FileInput source="files" label="Related files" multiple={true}>
<FileField source="src" title="title" />
</FileInput>
</SimpleForm>
</Create>
);
};
this is my App.js
const App = () => {
return (
<Admin authProvider={authProvider} dataProvider={myDataProvider}>
<Resource
name="contents"
list={PostList}
show={PostShow}
edit={PostEdit}
create={PostCreate}
/>
<Resource
name="themes"
list={ThemeList}
show={ThemeShow}
create={ThemeCreate}
/>
<Resource
name="curriculums"
list={CurriculumList}
show={CurriculumShow}
create={CurriculumCreate}
/>
</Admin>
);
};
export default App;
this is my Themes.js
export const ThemeShow = (props) => (
<Show {...props} title={<ThemeTitle />}>
<SimpleShowLayout>
<TextField source="id" />
<TextField source="title" />
<ArrayField source="contents">
<Datagrid>
<TextField source="id" />
<TextField source="title" />
<TextField source="category" />
<TextField source="type" />
<TextField source="description" />
</Datagrid>
</ArrayField>
<DateField label="Publication date" source="createdDate" />
</SimpleShowLayout>
</Show>
);
export const ThemeCreate = (props) => {
const validateInput = required();
const optionRenderer = (choice) => `${choice.id} (${choice.title})`;
return (
<Create {...props}>
<SimpleForm>
<TextInput source="title" validate={validateInput} />
<ReferenceInput
source="contentIds"
reference="contents"
validate={validateInput}>
<SelectArrayInput optionText={optionRenderer} />
</ReferenceInput>
</SimpleForm>
</Create>
);
};
this is my version
"react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"react-admin": {
"version": "3.16.4",
"resolved": "https://registry.npmjs.org/react-admin/-/react-admin-3.16.4.tgz",
"integrity": "sha512-l7HEFXmjwqcfuKV0eB3SFPjT/7kP7LjGci4YoPHmy9EFzNtR7uyh2WeCNmgSryTStyu6zKijE/2yRPdfvDhSUw==",
"requires": {
"#material-ui/core": "^4.11.2",
"#material-ui/icons": "^4.11.2",
"#material-ui/styles": "^4.11.2",
"connected-react-router": "^6.5.2",
"final-form": "^4.20.2",
"final-form-arrays": "^3.0.2",
"ra-core": "^3.16.3",
"ra-i18n-polyglot": "^3.16.3",
"ra-language-english": "^3.16.3",
"ra-ui-materialui": "^3.16.3",
"react-final-form": "^6.5.2",
"react-final-form-arrays": "^3.1.3",
"react-redux": "^7.1.0",
"react-router": "^5.1.0",
"react-router-dom": "^5.1.0",
"redux": "^3.7.2 || ^4.0.3",
"redux-saga": "^1.0.0"
}
},

Related

Autocompletearrayinput TypeError: Polyglot.transformPhrase expects argument #1 to be string

Hello stack overflow I was wondering if its possible to pre-populate with default values upon rendering. I'm also getting an error when using the from react admin. Here's my how i'm using my .
return (
<Edit {...props}>
<SimpleForm>
<TextInput source="audience_name" />
<ReferenceInput label="entity_ids" source="entity_ids" reference="posts">
<EntityInput setEntityLabel={onChangeLabel} onSelectEntity={addEntity} entityNames={entityNames} />
</ReferenceInput>
<br />
<AutocompleteArrayInput
source="tags"
shouldRenderSuggestions={(val) => {
console.log(val);
return val.trim().length > 0;
}}
choices={[
{ id: 'programming', name: 'Programming' },
{ id: 'lifestyle', name: 'Lifestyle' },
{ id: 'photography', name: 'Photography' },
]}
/>
</SimpleForm>
</Edit>
);
};
Try to add the props translateChoice={false}, like:
<AutocompleteInput source="first_name" choices={choices} translateChoice={false}/>
You can try recreating the i18nProvider, like this:
import polyglotI18nProvider from "ra-i18n-polyglot"; // Install this package
import engMessages from "ra-language-english"; // Install this package
const App = () => {
const i18nProvider = polyglotI18nProvider((locale) => engMessages, "en", {
allowMissing: true,
onMissingKey: (key, _, __) => key,
});
return (
<Admin
...
i18nProvider={i18nProvider}
>
)
}

Use the Datagrid component with custom queries - react-admin

Receive below errors, when using Datagrid component with custom queries. Below code works with react-admin ver 3.3.1, whereas it doesn't work with ver 3.8.1
TypeError: Cannot read property 'includes' of undefined
Browser's console info: List components must be used inside a <ListContext.Provider>. Relying on props rather than context to get List data and callbacks is deprecated and won't be supported in the next major version of react-admin.
Refer: https://marmelab.com/react-admin/List.html
#Tip: You can use the Datagrid component with custom queries:
import keyBy from 'lodash/keyBy'
import { useQuery, Datagrid, TextField, Pagination, Loading } from 'react-admin'
const CustomList = () => {
const [page, setPage] = useState(1);
const perPage = 50;
const { data, total, loading, error } = useQuery({
type: 'GET_LIST',
resource: 'posts',
payload: {
pagination: { page, perPage },
sort: { field: 'id', order: 'ASC' },
filter: {},
}
});
if (loading) {
return <Loading />
}
if (error) {
return <p>ERROR: {error}</p>
}
return (
<>
<Datagrid
data={keyBy(data, 'id')}
ids={data.map(({ id }) => id)}
currentSort={{ field: 'id', order: 'ASC' }}
basePath="/posts" // required only if you set use "rowClick"
rowClick="edit"
>
<TextField source="id" />
<TextField source="name" />
</Datagrid>
<Pagination
page={page}
perPage={perPage}
setPage={setPage}
total={total}
/>
</>
)
} ```
Please help!
Since react-admin 3.7, <Datagrid> and <Pagination> read data from a ListContext, instead of expecting the data to be injected by props. See for instance the updated <Datagrid> docs at https://marmelab.com/react-admin/List.html#the-datagrid-component.
Your code will work if you wrap it in a <ListContextProvider>:
import React, { useState } from 'react';
import keyBy from 'lodash/keyBy'
import { useQuery, Datagrid, TextField, Pagination, Loading, ListContextProvider } from 'react-admin'
export const CustomList = () => {
const [page, setPage] = useState(1);
const perPage = 50;
const { data, total, loading, error } = useQuery({
type: 'GET_LIST',
resource: 'posts',
payload: {
pagination: { page, perPage },
sort: { field: 'id', order: 'ASC' },
filter: {},
}
});
if (loading) {
return <Loading />
}
if (error) {
return <p>ERROR: {error}</p>
}
return (
<ListContextProvider value={{
data: keyBy(data, 'id'),
ids: data.map(({ id }) => id),
total,
page,
perPage,
setPage,
currentSort: { field: 'id', order: 'ASC' },
basePath: "/posts",
resource: 'posts',
selectedIds: []
}}>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="name" />
</Datagrid>
<Pagination />
</ListContextProvider >
)
}
<ReferenceManyField>, as well as other relationship-related components, also implement a ListContext. That means you can use a <Datagrid> of a <Pagination> inside this component.
https://marmelab.com/react-admin/List.html#uselistcontext
Your code should look like this:
import React, { useState } from 'react';
import keyBy from 'lodash/keyBy'
import { useQuery, Datagrid, TextField, Pagination, Loading, ListContextProvider } from 'react-admin'
export const CustomList = () => {
return (
<ReferenceManyField reference="Your resource for pull the data" target="linked field">
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="name" />
</Datagrid>
</ReferenceManyField>
)
}

How do I slow down my express server response to allow the react-admin getOne() function to work?

I have created VetCreate and VetEdit functions to create a new record and then edit that record respectively (code below). The problem I have is the successful create response is returned but the newly created id isn't populated in the request to fetch the record.
I have async/await keywords in place where I think they need to be but the details logged to the console clearly indicate something isn't working as it should. If I try to edit a record after returning to the list screen, the API works as expected and returns the details.
I've added all the code and screenshots I can think of but if anything more is required, please ask.
VetCreate
export const VetCreate = (props) => (
<Create title="Credit Application" aside={<CreateAside />} {...props}>
<TabbedForm toolbar={<VetCreateToolbar />} redirect="show">
<FormTab label="applicant">
<DateInput disabled label="Date Created" source="DateCreated" defaultValue={moment(new Date()).format('YYYY-MM-DD hh:mm:ss')} />
<TextInput disabled label="Agent Name" defaultValue={sessionStorage.getItem('foneBookUser')} source="CreatedBy" />
<TextInput label="First Name" defaultValue={'Adam'} source="FirstName" validate={validateLength} />
<TextInput label="Surname" defaultValue={'Smith'} source="Surname" validate={validateLength} />
<TextInput label="RSA ID Number" defaultValue={4567890987654} source="IDNumber" validate={validateID} />
<SelectInput label="Sex" defaultValue={'M'} source="sex" choices={[
{ id: 'M', name: 'Male' },
{ id: 'F', name: 'Female' },
]} validate={validateSex}/>
<TextInput label="Age of Applicant" defaultValue={45} source="ApplicantAge" validate={validateAge} />
<TextInput label="Business Partner" defaultValue={''} source="BusinessPartner" />
<TextInput label="Sales Person" defaultValue={'Spiderman'} source="SalesPerson" validate={validateLength} />
<TextInput label="Cell Phone Number" defaultValue={'345678'} source="CellNumber" validate={validateLength} />
<TextInput label="Email Address" defaultValue={'adam#email.com'} source="Email" validate={validateEmail} />
</FormTab>
<FormTab label="bureau">
<TextInput label="Bureau Score" defaultValue={123} source="BureauScore" validate={validateBureauScore} />
<TextInput label="Gross Monthly Income" defaultValue={30000} source="GrossMonthlyIncome" validate={validateGross} />
<TextInput label="Total Monthly Instalment" defaultValue={3000} source="TotalMonthlyInstalment" validate={validateInstal} />
<TextInput label="Home Postcode" defaultValue={'1122'} source="HomePostCode" validate={validateCode} />
<TextInput label="Number of Properties" defaultValue={11} source="NumProperties" validate={validateNumber} />
<TextInput label="Number of Companies" defaultValue={31} source="NumCompanies" validate={validateNumber} />
</FormTab>
</TabbedForm>
</Create>
);
VetEdit
export const VetEdit = props => {
const classes = useStyles();
return (
<Edit aside={<EditAside />} {...props}>
<SimpleForm toolbar={<VetEditToolbar />} warnWhenUnsavedChanges>
<TextInput disabled label="First Name" source="FirstName" formClassName={classes.inlineBlock} />
<TextInput disabled label="Surname" source="Surname" formClassName={classes.inlineBlock} />
<TextInput disabled label="RSA ID Number" source="IDNumber" formClassName={classes.inlineBlock} />
<TextInput disabled source="Sex" formClassName={classes.inlineBlock} />
<NumberInput disabled source="ApplicantAge" formClassName={classes.inlineBlock} />
<TextInput disabled source="CellNumber" formClassName={classes.inlineBlock} />
<TextInput disabled source="Email" formClassName={classes.inlineBlock} />
<TextInput disabled source="BusinessPartner" formClassName={classes.inlineBlock} />
<TextInput disabled source="SalesPerson" formClassName={classes.inlineBlock} />
<TextInput disabled source="HomePostCode" formClassName={classes.inlineBlock} />
<NumberInput disabled source="NumProperties" formClassName={classes.inlineBlock} />
<NumberInput disabled source="NumCompanies" formClassName={classes.inlineBlock} />
<NumberInput disabled source="GrossMonthlyIncome" formClassName={classes.inlineBlock} />
<NumberInput disabled source="TotalMonthlyInstalment" formClassName={classes.inlineBlock} />
<NumberInput disabled source="BureauScore" formClassName={classes.inlineBlock} />
<SelectInput label="ID Verified" source="IDVerified" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Debt Review" source="DebtReview" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Poor Payment Profile" source="PoorPaymentProfile" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Adverse" source="Adverse" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Fraud Suspected" source="SuspectFraud" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<TextInput label="ID Validated" source="IDValidated" formClassName={classes.inlineBlock} />
<TextInput label="Bank Statement Validated" source="BankStatementValidated" formClassName={classes.inlineBlock} />
<TextInput label="Payslip Validated" source="PayslipValidated" formClassName={classes.inlineBlock} />
<TextInput label="Proof of Residence Validated" source="ResProofValidated" formClassName={classes.inlineBlock} />
</SimpleForm>
</Edit>
)
};
DataProvider.js
import { fetchUtils } from 'react-admin';
import { stringify } from 'query-string';
const apiUrl = 'http://localhost:8081';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json'
});
}
// add your own headers here
options.headers.set(
'X-Custom-Header',
'Access-Control-Allow-Headers',
'Access-Control-Allow-Origin',
'*',
);
//console.log('4 options: ', options);
return fetchUtils.fetchJson(url, options);
};
export default {
getList: (resource, params) => {
//console.log('params: ', params);
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify(params.filter),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
//console.log('url: ', url);
return httpClient(url).then(({ headers, json }) => ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
}));
},
getOne: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
data: json,
})),
getMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ json }) => ({ data: json }));
},
getManyReference: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify({
...params.filter,
[params.target]: params.id,
}),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
}));
},
update: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json })),
updateMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json }));
},
create: (resource, params) =>
httpClient(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
})),
delete: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'DELETE',
}).then(({ json }) => ({ data: json })),
deleteMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'DELETE',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json }));
}
};
Vet API
Vet.createVet = async function(newVet, result) {
console.log('newVet: ', newVet);
await sql.query("INSERT INTO vets set ?", newVet, function(err, res) {
if(err) {
console.log('err: ', err);
result(err, null);
} else {
console.log('res.insertId: ', res.insertId);
result(null, res.insertId)
}
});
};
Vet.getVetById = async function (VetId, result) {
console.log('getVetById: ', VetId);
await sql.query("Select * from vets WHERE id = ?", VetId, function (err, res) {
if(err) {
console.log("error: ", err);
result(null, err);
} else {
console.log('vets : ', res);
result(null, res);
}
});
};
await only does something useful when you await a promise. Statements like this:
await sql.query("INSERT INTO vets set ?", newVet, function(err, res) {...});
are NOT awaiting a promise. That query does not return a promise. The result of that operation comes only in that callback, not in any returned promise so await has nothing to do. There is no magic in await that somehow knows when the plain callback is done. No, await operates on a promise and if you don't give it a promise, it has nothing to do (doesn't wait for anything).
Instead, you need to use database operations that DON'T use callbacks, but instead return promises if you want to use await with them.
If you're using mysql, see mysql2 for promise support and then rewrite all your database statements to use the returned promise to get the results from and do not pass the database call a plain callback.
In case anyone else runs into this problem and wasn't aware of the promise - async/await relationship, I found this article to be very helpful.

How can I set up a React Router auth flow?

I'm new to React and having trouble creating a simple auth flow using GraphQL, following the React Router example.
I would like / to be my login screen, which appears only if the user is logged out. If not, the app should redirect to /dashboard (which should otherwise be inaccessible). Here's what I have, which generates an infinite loop after authentication:
class App extends Component {
constructor(props) {
super(props);
this.state = {
authenticated: false
}
}
renderButtons () {
if (this.state.authenticated) {
return (
<a>Logout</a>
)
}
return null;
}
componentWillReceiveProps({ data }) {
if (this.props.data.loading && !data.loading) {
if (data.loggedInUser.id && !this.state.authenticated) {
this.setState({authenticated: true});
} else if (this.state.authenticated) {
this.setState({authenticated: false});
}
}
}
render () {
return (
<div className="container">
<nav>
{this.renderButtons()}
</nav>
<GatingRoute path="/" component={LoginPage} isAuthenticated={this.state.authenticated} newPath="/dashboard" />
<PrivateRoute path="/dashboard" component={Dashboard} isAuthenticated={this.state.authenticated} />
</div>
);
}
};
const PrivateRoute = ({ component: Component, isAuthenticated, ...rest}) => (
<Route
{...rest}
render={props => (
isAuthenticated
? (
<Component {...props} />
)
: (<Redirect to={{ pathname: '/', state: { from: props.location } }} />)
)}
/>
);
const GatingRoute = ({ component: Component, isAuthenticated, newPath, ...rest}) => (
<Route
{...rest}
render={props => (
isAuthenticated
? (
(<Redirect to={{ pathname: newPath, state: { from: props.location } }} />)
)
:
<Component {...props} />
)}
/>
);
export default graphql(query, { options: {fetchPolicy: 'network-only'}})(withRouter(App));
Rather than working the way you are now...
In your parent component where you have your routes do the following
<BrowserRouter>
<Route path=“/“ exact render={ ( props ) =>
props.user.loggedIn ? <Dashboard /> : <LoginForm />
}/>
</BrowserRouter>
This assumes the component that renders your router knows of the user status.
My apologies for the poor formatting on mobile.

React-Native error setState used in arrow function

I know was a thousand such questions relating to this same issue, but I've already exhausted ideas what might cause it in my case :/
I'm trying to update variable when after getting response from API.
I already changed all functions to use arrow function but I keep getting this error:
this.setState is not a function
and I can't find error in my code.
Can anyone see what's wrong?
app/routes/users/login/view.js
const UsersLoginView = (props) => {
let email = '';
this.state = {
textError: '',
inputError: false,
}
login = (event) => {
device_info = {
UUID: DeviceInfo.getUniqueID(),
brand: DeviceInfo.getBrand(),
model: DeviceInfo.getModel(),
system: DeviceInfo.getSystemName(),
system_version: DeviceInfo.getSystemVersion(),
timezone: DeviceInfo.getTimezone(),
locale: DeviceInfo.getDeviceLocale(),
}
fetch("my url", {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: email,
device: device_info
})
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({
textError: 'SUCCESS',
inputError: false
});
})
.catch(error => {
this.setState({
textError: error.message,
inputError: true
});
})
.done();
},
updateEmail = (text) => {
email = text
}
return (
<Container>
<Content>
<Text style={{fontWeight: 'bold'}}>
User
</Text>
<Form>
<Item error={this.state.inputError}>
<Icon active name='ios-at-outline' />
<Input
autoCorrect={false}
placeholder='E-mail'
returnKeyType='go'
keyboardType='email-address'
autoCapitalize='none'
onChangeText = { (text) => updateEmail(text) }
/>
</Item>
<Text>{this.state.textError}</Text>
<Button full light style={{marginTop: 20}} onPress={ () => login }>
<Text>Login</Text>
</Button>
</Form>
</Content>
</Container>
);
};
export default UsersLoginView;
app/routes/users/login/index.js
import UsersLoginView from './view';
class UsersLogin extends Component {
constructor(props) {
super(props);
}
render() {
return (
<UsersLoginView />
);
}
}
export default UsersLogin;
maybe login function should be in index.js file then I will have access to state ?
The error is here: const UsersLoginView = (props) => {, you need to use a class if you want to maintain state.