So I'm quite new to React native and I am making updates to an existing app. I have two dropdowns: a country dropdown and a city dropdown (These were originally text fields). When the country is set, the cities dropdown is populated from a JSON file. All this works okay, except when you change country the cities list is not populated immediately. If you set country twice - it sets the cities of the previous selection.
This is the relevant code in the page component:
[imports all in place]
const countries2 = require('../../common/constants/countries2.json');
const cities = require('../../common/constants/cities.json');
const validator = {
city: {
required: true,
string: true,
},
country: {
required: true,
string: true,
},
};
const RegistrationAddress = ({ route }) => {
const navigation = useNavigation();
const { personalDetails, socialRegistration } = route.params || {};
const [updateUser] = useMutation(UPDATE_PROFILE, {
fetchPolicy: 'no-cache',
});
const [state, setState] = useState({
city: '',
country: '',
personalDetails: personalDetails,
});
const [errors, setErrors] = useState({});
const [filteredCities, setCities] = useState([]);
const onChange = (field, val) => {
if (field === 'country') {
updateCityPicker();
}
const newState = { ...state, [field]: val };
setState(newState);
const err = validate(newState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
useEffect(() => {
const err = validate({}, validator);
setErrors(err);
getUserAddressDetails();
}, []);
const getUserAddressDetails = async () => {
const userAddressDetail = await AsyncStorage.getItem('userAddressDetails');
const userAddressDetails = JSON.parse(userAddressDetail);
const userAddressDetailsState = {
...state,
city: userAddressDetails.city || '',
country: userAddressDetails.country || '',
};
setState(userAddressDetailsState);
const err = validate(userAddressDetailsState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
const updateCityPicker = () => {
const suggestions = cities.filter((el) => {
var s = el[country];
return s;
});
var list = [];
suggestions.forEach((element) => {
element[country].forEach((cl) => {
var obj = {};
obj['label'] = cl;
obj['value'] = cl;
list.push(obj);
});
});
setCities(list);
};
const { city, country } = state;
const navigateToRegistrationConfirmDetails = () => {
if (socialRegistration) {
updateUser({
variables: {
data: {
city,
country,
},
},
}).then(async () => {
await AsyncStorage.removeItem('userBasicDetails');
await AsyncStorage.removeItem('userAddressDetails');
navigation.navigate('QuestionnaireIntroduction');
});
} else {
navigation.navigate('RegistrationConfirmDetails', { userDetails: state });
}
};
return (
<ImageBackground style={styles.imageBackground}>
<View style={styles.regStepView}>
<Text style={styles.regStep}>
Address
<Text style={styles.oneOutOfThree}> - 3/3</Text>
</Text>
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<KeyboardAvoidingView style={styles.inputsView}>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={country}
onValueChange={(val) => onChange('country', val)}
testID="countryID"
placeholder="eg. United Kingdom"
items={countries2}
checkDropdownErrors={false}
error={errors.accountCurrency && errors.accountCurrency[0]}
showDropdownError={''}
title="Which country do you currently live in?"
/>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={city}
onValueChange={(val) => onChange('city', val)}
testID="cityID"
placeholder="eg. London"
items={filteredCities}
checkDropdownErrors={false}
error={errors.city && errors.city[0]}
showDropdownError={''}
title="Which city do you currently live in?"
/>
</KeyboardAvoidingView>
</ScrollView>
<View>
<Button
disabled={Object.keys(errors).length}
styleContainer={{ marginBottom: scale(24) }}
title="Next"
onPressFunc={async () => {
await AsyncStorage.setItem(
'userAddressDetails',
JSON.stringify(state),
)
.then(() => navigateToRegistrationConfirmDetails())
.catch((err) => console.log({ err }));
}}
/>
</View>
</ImageBackground>
);
};
export default RegistrationAddress;
I also get a warning about setting the state of a component from within another component, which is understandable, but I don't know the solution.
Any help would be hugely appreciated. Sorry if it's an existing question - other answers didn't quite work for me.
Problem is setState is async. Its order is not guaranteed. So in your case its good to keep single useState as you are already using dictionary. Idea is that when you change country cityList will be filtered according to country else its empty. I have updated code as below, try it. It should work for you.
Explanation: I have introduced new cityList attribute which will be updated in case of country is changed. Also to display dropdown list we use the same cityList attribute for city dropdown.
[imports all in place]
const countries2 = require('../../common/constants/countries2.json');
const cities = require('../../common/constants/cities.json');
const validator = {
city: {
required: true,
string: true,
},
country: {
required: true,
string: true,
},
};
const RegistrationAddress = ({ route }) => {
const navigation = useNavigation();
const { personalDetails, socialRegistration } = route.params || {};
const [updateUser] = useMutation(UPDATE_PROFILE, {
fetchPolicy: 'no-cache',
});
const [state, setState] = useState({
city: '',
country: '',
cityList: [],
personalDetails: personalDetails,
});
const [errors, setErrors] = useState({});
const onChange = (field, val) => {
let cityList = state.cityList;
if (field === 'country') {
cityList = cities.filter((el) => {
var s = el[country];
return s;
});
}
const newState = { ...state, [field]: val, cityList };
setState(newState);
const err = validate(newState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
useEffect(() => {
const err = validate({}, validator);
setErrors(err);
getUserAddressDetails();
}, []);
const getUserAddressDetails = async () => {
const userAddressDetail = await AsyncStorage.getItem('userAddressDetails');
const userAddressDetails = JSON.parse(userAddressDetail);
const userAddressDetailsState = {
...state,
city: userAddressDetails.city || '',
country: userAddressDetails.country || '',
};
setState(userAddressDetailsState);
const err = validate(userAddressDetailsState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
const updateCityPicker = () => {
const suggestions = cities.filter((el) => {
var s = el[country];
return s;
});
var list = [];
suggestions.forEach((element) => {
element[country].forEach((cl) => {
var obj = {};
obj['label'] = cl;
obj['value'] = cl;
list.push(obj);
});
});
setCities(list);
};
const { city, country, cityList } = state;
const navigateToRegistrationConfirmDetails = () => {
if (socialRegistration) {
updateUser({
variables: {
data: {
city,
country,
},
},
}).then(async () => {
await AsyncStorage.removeItem('userBasicDetails');
await AsyncStorage.removeItem('userAddressDetails');
navigation.navigate('QuestionnaireIntroduction');
});
} else {
navigation.navigate('RegistrationConfirmDetails', { userDetails: state });
}
};
return (
<ImageBackground style={styles.imageBackground}>
<View style={styles.regStepView}>
<Text style={styles.regStep}>
Address
<Text style={styles.oneOutOfThree}> - 3/3</Text>
</Text>
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<KeyboardAvoidingView style={styles.inputsView}>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={country}
onValueChange={(val) => onChange('country', val)}
testID="countryID"
placeholder="eg. United Kingdom"
items={countries2}
checkDropdownErrors={false}
error={errors.accountCurrency && errors.accountCurrency[0]}
showDropdownError={''}
title="Which country do you currently live in?"
/>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={city}
onValueChange={(val) => onChange('city', val)}
testID="cityID"
placeholder="eg. London"
items={cityList}
checkDropdownErrors={false}
error={errors.city && errors.city[0]}
showDropdownError={''}
title="Which city do you currently live in?"
/>
</KeyboardAvoidingView>
</ScrollView>
<View>
<Button
disabled={Object.keys(errors).length}
styleContainer={{ marginBottom: scale(24) }}
title="Next"
onPressFunc={async () => {
await AsyncStorage.setItem(
'userAddressDetails',
JSON.stringify(state),
)
.then(() => navigateToRegistrationConfirmDetails())
.catch((err) => console.log({ err }));
}}
/>
</View>
</ImageBackground>
);
};
export default RegistrationAddress;
Related
I am trying to access the students variable by putting its value in a state then accessing that state in my handler function but it always returns an empty array.
const students = useSelector((state) => state.students);
useEffect(() => {
setdata(students);
console.log(data);
}, [])
const handleScanWebCam = (result, error) => {
if (result) {
console.log(data)
const list = data.filter(function (student) {
return student.id === result.text;
})
setScanResultWebCam({
id: list.id,
grade: list.grade,
name: list.name,
section: list.section
});
}
}
this is my full code
function QrScanner() {
const dispatch = useDispatch(getStudents())
useEffect(() => {
dispatch(getStudents());
}, [dispatch])
const students = useSelector((state) => state.students);
const [scanResultWebCam, setScanResultWebCam ] = useState({
id: '',
grade: '',
name: '',
section:''
});
const [openVerifier, setOpenVerifier] = useState(false);
const [data, setdata] = useState([]);
useEffect(() => {
setdata(students);
console.log(data);
}, [])
const handleScanWebCam = (result, error) => {
if (result) {
const list = data.filter(function (student) {
return student.id === result.text;
})
setScanResultWebCam({
id: list.id,
grade: list.grade,
name: list.name,
section: list.section
});
setOpenVerifier(true);
}
}
return (
<>
<NavBar />
<Container
sx={{
display: 'flex',
marginTop: '4rem',
flexWrap: 'wrap',
gap: '12px',
width: '90%'
}}
>
<Box
sx={{
width: '50%',
border: 'solid',
display: 'flex',
flex: '1'
}}
>
<QrReader
scanDelay={500}
containerStyle={{ width: '25rem', margin: 'auto'}}
onResult={handleScanWebCam}
// onError={handleErrorWebcam}
/>
</Box>
<PopupVerifier
details={scanResultWebCam}
verifier={openVerifier}
handleClose={() => handleClose()}
/>
</Container>
</>
)
}
If you need to cache a "duplicate" of the selected students state then I'd recommend caching students in a React ref that can have its current value read at any time during the component lifecycle, especially in stale closures.
Example:
function QrScanner() {
const dispatch = useDispatch(getStudents());
useEffect(() => {
dispatch(getStudents());
}, [dispatch]);
const students = useSelector((state) => state.students);
const studentsRef = React.useRef(students);
useEffect(() => {
studentsRef.current = students;
}, [students]);
...
const handleScanWebCam = (result, error) => {
if (result) {
const list = studentsRef.current.find(
(student) => student.id === result.text
);
if (list) {
setScanResultWebCam(list);
}
setOpenVerifier(true);
}
};
return (
...
);
}
You have using from react-redux. so you dont need to set student state.
and dispatch is used for change in store not getting data from it.
if you want to get an item from array use find instead of filter :
const list = students.find(student=>student.id===result.text);
i edit your code to :
function QrScanner() {
const students = useSelector((state) => state.students);
const [scanResultWebCam, setScanResultWebCam ] = useState({
id: '',
grade: '',
name: '',
section:''
});
const [openVerifier, setOpenVerifier] = useState(false);
const handleScanWebCam = (result, error) => {
if(result) {
const list = students.find(student=> student.id === result.text);
setScanResultWebCam({id: list.id, grade: list.grade, name: list.name, section: list.section});
setOpenVerifier(true);
}
}
return(
...
)
}
I have a problem with the following exportHtml function, this must export the html content of a react component but I can't understand why it doesn't enter the function to extrapolate html, how can I solve it?
Editor.js
import EmailEditor.js
const Example = () => {
const emailEditorRef = useRef(null);
const [titolo, setTitolo] = useState('');
const [getHtml, setHtml] = useState('');
const [newsProfileValue, setNewsProfileSet] = useState('');
const [profileList, setNewsProfileList] = useState([]);
const [premiumValue, setPremium] = useState(false);
const [publicValue, setPublic] = useState(false);
const arr = [];
const newsCollectionRef = collection(db, 'news');
const saveDesign = () => {
emailEditorRef.current.editor.saveDesign((design) => {
exportData(design);
});
};
const getList = async () => {
try {
var id = await localStorage.getItem('uid');
console.log(id);
var data = [];
const querySnapshot = await getDocs(collection(db, 'news_profiles'));
querySnapshot.forEach((doc) => {
if (doc.data().about.uid == id) {
data.push({ title: doc.data().about.title, id: doc.id });
arr.push({ doc: doc.data().about.title, id: doc.id });
}
});
} catch (error) {
console.log('Error: ' + error);
}
var options = arr.map((e) => {
return `<option value="${e.id}">${e.doc}</option>`;
});
document.getElementById('selectNumber').innerHTML = options;
setNewsProfileList(data);
};
const exportData = (design) => {
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
JSON.stringify(design)
)}`;
const link = document.createElement('a');
link.href = jsonString;
link.download = 'data.json';
link.click();
};
const exportHtml = () => {
console.log('Sono in html');
emailEditorRef.current.editor.exportHtml((data) => {
const { design, html } = data;
setHtml(html);
});
addElementToFireStoreNews(getHtml);
};
const addElementToFireStoreNews = async (html) => {
try {
console.log('Sono in addElementToFireStoreNews ');
var uuid = await localStorage.getItem('uid');
var jsonFile;
var templateid = await localStorage.getItem('templateid');
if (templateid == 1) {
jsonFile = sample;
} else if (templateid == 2) {
jsonFile = sample3;
} else {
jsonFile = sample2;
}
console.log('Setup options');
const options = {
method: 'POST',
url: 'http://link',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer kjsdgu6523kjbnap!w',
},
data: {
internal: true,
online: publicValue,
premium: premiumValue,
html: html,
template: jsonFile,
subject: html,
email: uuid,
},
};
console.log('Chiamo il rest');
axios
.request(options)
.then(function (response) {
console.log('data: ' + response.data);
})
.catch(function (error) {
console.error('Errore axios: ' + error);
});
} catch (err) {
console.log('Errore inserimnento: ' + err);
}
};
const onDesignLoad = (data) => {
getList();
console.log('onDesignLoad', data);
};
const onLoad = async () => {
var templateid = await localStorage.getItem('templateid');
if (templateid == 1) {
emailEditorRef.current.editor.loadDesign(sample);
} else if (templateid == 2) {
emailEditorRef.current.editor.loadDesign(sample3);
} else {
emailEditorRef.current.editor.loadDesign(sample2);
}
emailEditorRef.current.editor.addEventListener(
'design:loaded',
onDesignLoad
);
};
const onReady = () => {};
const changeText = (text) => {
setTitolo(text);
};
const clickValue = (e) => {
setNewsProfileSet(e.target.value);
};
const changeValue = () => {
setPremium(!premium);
};
const changeValuePublic = () => {
setPublic(!publicValue);
};
return (
<Container>
<Bar>
<img src="firebase.png" width="100" height="50" />
<label style={{ color: 'white' }}>
<b>Titolo News: </b>
<input
type="text"
name="name"
onChange={(value) => changeText(value.target.value)}
style={{ marginRight: 200 }}
/>
Premium:{' '}
<Checkbox
{...label}
unchecked
onClick={() => changeValue()}
style={{ color: 'white' }}
/>
Online:{' '}
<Checkbox
{...label}
unchecked
onClick={() => changeValuePublic()}
style={{ color: 'white' }}
/>
</label>
<form id="myForm">
<select id="selectNumber" onClick={clickValue}>
<option>Scegli il news profile</option>
</select>
<button onClick={() => saveDesign}> Salva Design </button>
<button onClick={() => exportHtml()}> Salva Articolo </button>{' '}
</form>
</Bar>
<React.StrictMode>
<EmailEditor ref={emailEditorRef} onLoad={onLoad} onReady={onReady} />{' '}
</React.StrictMode>{' '}
</Container>
);
};
export default Example;
EmailEditor.js
import React, { Component } from 'react';
import { loadScript } from './loadScript';
import pkg from '../package.json';
let lastEditorId = 0;
export default class extends Component {
constructor(props) {
super(props);
this.editorId = props.editorId || `editor-${++lastEditorId}`;
}
componentDidMount() {
loadScript(this.loadEditor, this.props.scriptUrl);
}
render() {
let {
props: { minHeight = 500, style = {} },
} = this;
return (
<div
style={{
flex: 1,
display: 'flex',
minHeight: minHeight,
}}
>
<div id={this.editorId} style={{ ...style, flex: 1 }} />
</div>
);
}
loadEditor = () => {
if (this.loaded) return;
this.loaded = true;
const options = this.props.options || {};
if (this.props.projectId) {
options.projectId = this.props.projectId;
}
if (this.props.tools) {
options.tools = this.props.tools;
}
if (this.props.appearance) {
options.appearance = this.props.appearance;
}
if (this.props.locale) {
options.locale = this.props.locale;
}
this.editor = unlayer.createEditor({
...options,
id: this.editorId,
displayMode: 'email',
source: {
name: pkg.name,
version: pkg.version,
},
});
for (const [key, value] of Object.entries(this.props)) {
if (/^on/.test(key) && key !== 'onLoad' && key !== 'onReady') {
this.addEventListener(key, value);
}
}
const { onLoad, onReady } = this.props;
// #deprecated
onLoad && onLoad();
if (onReady) this.editor.addEventListener('editor:ready', onReady);
};
registerCallback = (type, callback) => {
this.editor.registerCallback(type, callback);
};
addEventListener = (type, callback) => {
this.editor.addEventListener(type, callback);
};
loadDesign = (design) => {
this.editor.loadDesign(design);
};
saveDesign = (callback) => {
this.editor.saveDesign(callback);
};
exportHtml = (callback) => {
this.editor.exportHtml(callback);
};
setMergeTags = (mergeTags) => {
this.editor.setMergeTags(mergeTags);
};
}
I am trying to add some 'save preferences' functionality to a DataGrid using https://reactdatagrid.io/docs/miscellaneous but when I add sortInfo makes columns unsortable, the same happens for filterValue (trying to save filtering strings/data). Code here:
import React, { useCallback, useState } from 'react';
import DataGrid from '#inovua/reactdatagrid-enterprise';
import { columnFilters, eeOverviewColumns, filterTypes } from "./overview-columns";
import '#inovua/reactdatagrid-enterprise/index.css';
import { TypeRowProps, TypeRowSelection } from '#inovua/reactdatagrid-community/types';
import { TypeOnSelectionChangeArg } from "#inovua/reactdatagrid-community/types/TypeDataGridProps"
import { Button, FormControl, MenuItem, Select, SelectChangeEvent, TextField } from '#mui/material';
import { TypeColumn, TypeFilterValue, TypeSortInfo } from '#inovua/reactdatagrid-enterprise/types';
interface StoreLayout {
columns: TypeColumn[];
sortInfo: TypeSortInfo;
columnOrder : string[];
filterValue: TypeFilterValue;
}
let STORE: StoreLayout = {
columns: eeOverviewColumns,
sortInfo: [],
columnOrder: eeOverviewColumns.map(ee => ee.name) as string[],
filterValue: columnFilters,
}
let VIEWS= [
{
id: 0,
name: "Default view",
state: STORE
}
]
const EEOverview = (props: any) => {
const dataSource = props.eeData
// Checkbox selection
const [selected, setSelected] = useState<TypeRowSelection>();
const onSelectionChange = useCallback(
(config: TypeOnSelectionChangeArg) => {
setSelected(config.selected)
},
[],
);
const goToEe = useCallback((rowProps: TypeRowProps) => {
window.location.href = `${window.location.href}/${rowProps.data.key}`;
}, [])
const initialState = Object.assign({}, STORE, {});
const [state, setState] = useState(initialState);
const [viewName, setViewName] = useState('')
const sendViewName = (viewName: string) => { setViewName(viewName) }
const saveState = () => {
if (!viewName || viewName.length === 0 ) {
alert("View name not provided")
return
}
STORE = {
columnOrder: state.columnOrder,
columns: state.columns,
sortInfo: state.sortInfo,
filterValue: state.filterValue
}
setState(Object.assign({}, state, {}))
if(VIEWS.map(view => view.name).some(name => name === viewName)) {
const view = VIEWS.find(view => view.name === viewName)!
view.state = state
} else {
VIEWS.push({
id: VIEWS.length,
name: viewName,
state: state
})
}
}
const onSortInfoChange = (sortInfo: TypeSortInfo) => {
setState(Object.assign({}, state, { sortInfo }));
}
const onColumnOrderChange = (columnOrder: string[]) => {
setState(Object.assign({}, state, { columnOrder }));
}
const onFilterValueChange = (filterValue: TypeFilterValue) => {
setState(Object.assign({}, state, { filterValue }));
}
const onBatchColumnResize = (batchColumnInfo: any, { reservedViewportWidth }: any) => {
const colsMap = batchColumnInfo.reduce((acc: any, colInfo: any) => {
const { column, width, flex } = colInfo
acc[column.name] = { width, flex }
return acc
}, {})
const columns = state.columns.map((c: any) => {
return Object.assign({}, c, colsMap[c.name])
})
setState(Object.assign({}, state, {
columns,
reservedViewportWidth
}))
}
return (
<div>
<ViewSelector state = {state} onChange = {setState} ></ViewSelector>
<ViewText onChange = {sendViewName} ></ViewText>
<Button sx={{ mx: 2, my: 2, minWidth: 80 }} variant="contained"
onClick = {saveState}
>
Save view
</Button>
<DataGrid
idProperty="key"
theme="default-light"
className="data-grid"
defaultFilterValue={columnFilters}
filterTypes={filterTypes}
filterValue={state.filterValue} //<- here
onRowClick={goToEe}
columns={state.columns}
sortInfo={state.sortInfo} //<- and here
columnOrder={state.columnOrder}
pagination="local"
dataSource={dataSource}
onSelectionChange={onSelectionChange}
sortable={true}
checkboxColumn
selected={selected}
enableSelection={true}
onSortInfoChange={onSortInfoChange}
onBatchColumnResize={onBatchColumnResize}
onColumnOrderChange={onColumnOrderChange}
onFilterValueChange={onFilterValueChange}
/>
</div>
);
}
export default EEOverview;
const ViewText = ({onChange}: {onChange: any}) => {
const onViewNameChange = (viewName: string) => {
onChange(viewName)
}
return (
<TextField sx={{ mx: 2, my: 2 }}
id="search"
variant="outlined"
size="small"
label="Name current view"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { onViewNameChange(e.target.value) }}
/>
)
}
const ViewSelector = ({state, onChange}: {state:any, onChange: any}) => {
const [selectedView, setSelectedView] = useState(0)
const handleViewChange = (event: SelectChangeEvent<number>) => {
const selectedView = VIEWS.find(view => view.id===event.target.value)!
setSelectedView(selectedView.id)
const selectedState = selectedView.state
onChange(Object.assign({},selectedState, {}))
}
return (
<FormControl sx={{ m: 2, minWidth: 140 }} >
<Select labelId="view" variant="standard" size="medium" value={selectedView}
renderValue={(selected: any) => { return <div>{VIEWS[selected].name}</div>; }}
onChange={handleViewChange}>
{VIEWS.map((val, key) => (
<MenuItem value={key}>{val.name}</MenuItem>
))}
</Select>
</FormControl>
)
}
If I remove sortInfo/filterValue from passing to DataGrid, it behaves correctly, but it won't be saved to preferences.
Tried to move dataSource from props to state but it has the same behaviour
There are two Select field where am getting data in drop down . Now when I am changing the value and selecting other value its throwing error .
Please suggest me how can I change these value dynamically and submit to server .On button click I have to update this to server . Please help me for that .
class UpdateBillPreferences extends Component {
constructor(props) {
super(props);
const {navigation,clmmasterData} =this.props;
this.state = {
title: 'Update Bill Preferences',
mobile: navigation.state.params.customer.service.serviceNumber,
icon: 'sim',
email:'',
smsNum:'',
faxNum:'',
preferredLanguage: navigation.state.params.customerInfo[0].billingPreferenceDetails.presentationLanguageCode,
preferredCurrency: navigation.state.params.customerInfo[0].billingPreferenceDetails.preferedCurrencyCode,
};
this.presentationLanguageOptions=[{value:'english',label:'English'}, {value:'spanish',label:'Spanish'}]
this.preferredCurrencyOptions=[{value:'dollar',label:'Dollar'}, {value:'niara',label:'Niara'}]
}
componentDidMount() {
}
OnButtonClick = async (preferredLanguage, preferredCurrency,email,smsNum,faxNum) => {
const { OnButtonClick } = this.props;
await OnButtonClick(preferredLanguage, preferredCurrency,email,smsNum,faxNum);
this.setState({
preferredLanguage: '',
preferredCurrency:'',
email :'',
smsNum :'',
faxNum :''
})
}
simRegionChanged = (val, target) => {
this.handleChange({ field: "preferredLanguage" }, target);
};
handleChange = (props, e) => {
let tempObj = this.state.presentationLanguageCode;
tempObj[props.field] = e;
this.setState({ presentationLanguageCode: tempObj });
};
render() {
let { title, mobile, icon } = this.state;
const { navigation,clmmasterData} = this.props;
const {billingAddressDetails,billingPreferenceDetails} = navigation.state.params.customerInfo[0];
const {masterData , language} = clmmasterData;
let submitBtn = { label: 'Submit', OnSubmit: this.onSubmit };
let currencyData=[];
masterData.preferredCurrency.map(({ code: value, name: label }) => {
currencyData.push({ value, label });
});
let languageData=[];
masterData.language.map(({ code: value, name: label }) => {
languageData.push({ value, label });
});
return (
<ImageBackground source={BG} style={styles.imgBG}>
<ScrollView>
<View style={styles.container}>
<View>
<Header title={title} subtitle={mobile} icon={icon} navigation={navigation}/>
</View>
<View style={styles.contentContainer}>
<View style={{ padding: 20 }}>
<Form style={{ width: '100%' }}>
<SelectField
label="Presentation Language"
node="presentationLanguage"
options={languageData}
value={this.state.preferredLanguage}
onChange={this.simRegionChanged}
that={this}
setIcon={true}
img="LANGUAGE"
/>
<SelectField
label="Preferred Currency"
options={currencyData}
value={this.state.preferredCurrency}
node="preferredCurrency"
onChange={this.handleChange}
that={this}
setIcon={true}
img="CURRENCY"
/>
<PrimaryBtn label={'submit'} disabled={false} onPress={()=> this.OnButtonClick(this.state.preferredLanguage,this.state.preferredCurrency,
Thanks
Your problem is here:
simRegionChanged = (val, target) => {
this.handleChange({ field: "preferredLanguage" }, target);
};
handleChange = (props, e) => {
let tempObj = this.state.presentationLanguageCode;
tempObj[props.field] = e;
this.setState({ presentationLanguageCode: tempObj });
};
You are calling handleChange with the prop {field: "preferredLanguage"}. The first line of handleChange then returns undefined because presentationLanguageCode isn't in your state, so when you then do this:
tempObj[props.field] = e; // tempObj["preferredLanguage"] = e
... it throws the error because you're attempting to set a property on an object that doesn't exist.
It looks like you should just be updating preferredLanguage:
handleChange = (props, e) => {
this.setState({ preferredLanguage: e });
};
if i write this onPress={() => this.submit()} instead onPress={() => this.onSendBtnPressed()} i can get the text value but if i just write onPress={() => this.onSendBtnPressed()} this i can't see the text on the message list
here is my chatui
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {KeyboardAvoidingView, Image, TouchableOpacity, ReactNative,
StyleSheet } from 'react-native';
import {Footer, FooterTab} from "native-base";
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-
view';
import { View, Title, Screen, ScrollView, TextInput,Text } from
'#shoutem/ui';
import Messages from '../containers/Messages';
import Input from '../containers/Input';
import { sendMessage } from '../actions';
const mapStateToProps = (state) => ({
chatHeight: state.chatroom.meta.height,
user: state.user,
});
class ChatUI extends Component {
constructor()
{
super();
this.state = {
text: '',
scrollViewHeight: 0,
inputHeight: 0
}
}
updateValue(text) {
console.warn(text)
}
componentDidMount() {
this.scrollToBottom(false);
}
componentDidUpdate() {
this.scrollToBottom();
}
onChangeText(text) {
this.setState({text: text})
};
onSendBtnPressed (text) {
return sendMessage(text, this.props.user)
this.textInput.clear();
Keyboard.dismiss();
}
onScrollViewLayout = (event) => {
const layout = event.nativeEvent.layout;
this.setState({
scrollViewHeight: layout.height
});
}
onInputLayout = (event) => {
const layout = event.nativeEvent.layout;
this.setState({
inputHeight: layout.height
});
}
scrollToBottom(animate = false) {
const { scrollViewHeight, inputHeight } = this.state,
{ chatHeight } = this.props;
const scrollTo = chatHeight - scrollViewHeight + inputHeight;
if (scrollTo > 0) {
this.refs.scroll.scrollToEnd();
}
}
_scrollToInput(reactRef) {
this.refs.scroll.scrollToFocusedInput(ReactNative.findNodeHandle(reactRef));
}
submit() {
let collection = {}
collection.text = this.state.text,
console.warn(collection);
}
render() {
return (
<Screen >
<Title style={{paddingTop: 25, paddingLeft: 10, borderBottomWidth: 0.5, backgroundColor: 'white', paddingBottom: 10}}>
<Image style={{width:80, height: 80}} source={require('../img/11.png')} />
<Text> GENERAL CHAT</Text>
</Title>
<KeyboardAwareScrollView ref="scroll"
onLayout={this.onScrollViewLayout}>
<Messages />
</KeyboardAwareScrollView>
<Footer style={{width:360,
height:30,
backgroundColor:'#fff',
borderRadius:20,
borderWidth: 0.5,
marginBottom: 3,
borderColor: 'gray',
}}>
<TextInput
onSubmitEditing = {this.onSubmitEditing}
multiline
onLayout={this.onInputLayout}
submitAction={this.onSendBtnPressed}
ref="input"
placeholder="Say something ..."
onChangeText={(text) => this.onChangeText(text, 'text')}
style={{backgroundColor: 'rgba(0,0,0,0)',
borderBottomWidth: 1.5, borderColor: '#f8f8f8', fontSize: 16, color: 'gray', paddingBottom:20}}
ref={input => { this.textInput = input; } }/>
</Footer>
<TouchableOpacity
onPress={() => this.onSendBtnPressed()}
style={{marginLeft:280, backgroundColor:'gray', width: 70, height: 30}}
title= "send"
>
</TouchableOpacity>
</Screen>
)
}
}
export default connect(mapStateToProps, {sendMessage})(ChatUI);
and here is my actions
import firebase from '../firebase';
import DeviceInfo from 'react-native-device-info';
import {Actions} from 'react-native-router-flux';
import FCM, { FCMEvent, NotificationType, WillPresentNotificationResult,
RemoteNotificationResult } from 'react-native-fcm';
import { Platform } from 'react-native';
export const addMessage = (msg) => ({
type: 'ADD_MESSAGE',
...msg
});
export const sendMessage = (text, user) => {
return function (dispatch) {
let msg = {
text: text,
time: Date.now(),
author: {
name: user.name,
avatar: user.avatar
}
};
const newMsgRef = firebase.database()
.ref('messages')
.push();
msg.id = newMsgRef.key;
newMsgRef.set(msg);
dispatch(addMessage(msg));
};
};
export const startFetchingMessages = () => ({
type: 'START_FETCHING_MESSAGES'
});
export const receivedMessages = () => ({
type: 'RECEIVED_MESSAGES',
receivedAt: Date.now()
});
export const fetchMessages = () => {
return function (dispatch) {
dispatch(startFetchingMessages());
firebase.database()
.ref('messages')
.orderByKey()
.limitToLast(20)
.on('value', (snapshot) => {
setTimeout(() => {
const messages = snapshot.val() || [];
dispatch(receiveMessages(messages))
}, 0);
});
}
}
export const receiveMessages = (messages) => {
return function (dispatch) {
Object.values(messages).forEach(msg => dispatch(addMessage(msg)));
dispatch(receivedMessages());
}
}
export const updateMessagesHeight = (event) => {
const layout = event.nativeEvent.layout;
return {
type: 'UPDATE_MESSAGES_HEIGHT',
height: layout.height
}
}
export const setUserName = (name) => {
return (dispatch) => {
dispatch({
type: 'SET_USER_NAME',
payload: name
});
};
};
export const setUserAvatar = (avatar) => ({
type: 'SET_USER_AVATAR',
avatar: avatar && avatar.length > 0 ? avatar : 'avatar.jpg'
});
export const login = () => {
return function (dispatch, getState) {
dispatch(startAuthorizing());
firebase.auth()
.signInAnonymously()
.then(() => {
const { name, avatar } = getState().user;
firebase.database()
.ref(`users/`)
.push({
name,
avatar
});
dispatch(userAuthorized());
dispatch(fetchMessages());
});
}
}
export const checkUserExists = () => {
return function (dispatch) {
dispatch(startAuthorizing());
firebase.auth()
.signInAnonymously()
.then(() => firebase.database()
.ref(`users/${DeviceInfo.getUniqueID()}`)
.once('value', (snapshot) => {
const val = snapshot.val();
if (val === null) {
dispatch(userNoExist());
}else{
dispatch(setUserName(val.name));
dispatch(setUserAvatar(val.avatar));
startChatting(dispatch);
}
}))
.catch(err => console.log(err))
}
}
const startChatting = function (dispatch) {
dispatch(userAuthorized());
dispatch(fetchMessages());
FCM.requestPermissions();
FCM.getFCMToken()
.then(token => {
console.log(token)
});
FCM.subscribeToTopic('secret-chatroom');
FCM.on(FCMEvent.Notification, async (notif) => {
console.log(notif);
if (Platform.OS === 'ios') {
switch (notif._notificationType) {
case NotificationType.Remote:
notif.finish(RemoteNotificationResult.NewData); //other
types available: RemoteNotificationResult.NewData,
RemoteNotificationResult.ResultFailed
break;
case NotificationType.NotificationResponse:
notif.finish();
break;
case NotificationType.WillPresent:
notif.finish(WillPresentNotificationResult.All); //other
types available: WillPresentNotificationResult.None
break;
}
}
});
FCM.on(FCMEvent.RefreshToken, token => {
console.log(token);
});
}
export const startAuthorizing = () => ({
type: 'USER_START_AUTHORIZING'
});
export const userAuthorized = () => ({
type: 'USER_AUTHORIZED'
});
export const userNoExist = () => ({
type: 'USER_NO_EXIST'
});
and MessageList
import React, { Component } from 'react';
import {
ListView, Text, Row, Image,
View, Subtitle, Caption, Heading
} from '#shoutem/ui';
import moment from 'moment';
const Message = ({ msg }) => (
<Row>
<Image styleName="small-avatar top"
source={{ uri: msg.author.avatar }} />
<View styleName="vertical">
<View styleName="horizontal space-between">
<Subtitle>{msg.author.name}</Subtitle>
<Caption>{moment(msg.time).from(Date.now())}</Caption>
</View>
<Text styleName="multiline">{msg.text}</Text>
</View>
</Row>
);
const MessageList = ({ messages, onLayout }) => (
<ListView data={messages}
autoHideHeader={true}
renderRow={msg => <Message msg={msg} />}
onLayout={onLayout}
/>
);
export default MessageList;
You are not passing a text variable into your onSendButtonPressed function. It looks like you should be calling it using the following syntax:
onPress={() => this.onSendBtnPressed(someText)}
Your onSendBtnPressed() function has the return statement on the first line, so the code will not be fired underneath the return statement (not related to your question, but hope to fix any other headaches...)