Component looping over infinitely after updating state - javascript

I'm trying to update state under a promise. But the component is going over and over again infinitely until it max out the heap size. I don't know what I'm missing here.
This is my code
import {useDropzone} from 'react-dropzone';
import File from './File'
import parser from 'subtitles-parser'
const baseStyle = {
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '20px',
borderWidth: 2,
borderRadius: 2,
borderColor: '#eeeeee',
borderStyle: 'dashed',
backgroundColor: '#fafafa',
color: '#bdbdbd',
outline: 'none',
transition: 'border .24s ease-in-out'
};
const activeStyle = {
borderColor: '#2196f3'
};
const acceptStyle = {
borderColor: '#00e676'
};
const rejectStyle = {
borderColor: '#ff1744'
};
function Drag(props) {
const {
getRootProps,
getInputProps,
isDragActive,
isDragAccept,
isDragReject,
acceptedFiles
} = useDropzone();
const style = useMemo(() => ({
...baseStyle,
...(isDragActive ? activeStyle : {}),
...(isDragAccept ? acceptStyle : {}),
...(isDragReject ? rejectStyle : {})
}), [
isDragActive,
isDragReject
]);
const [data, setData] = useState(null)
if(acceptedFiles.length === 1){
const readUploadedFileAsText = (acceptedFiles) => {
const temporaryFileReader = new FileReader();
return new Promise((resolve, reject) => {
temporaryFileReader.onerror = () => {
temporaryFileReader.abort();
reject(new DOMException("Problem parsing input file."));
};
temporaryFileReader.onload = () => {
resolve(parser.fromSrt(temporaryFileReader.result));
};
temporaryFileReader.readAsText(acceptedFiles);
});
};
let file = acceptedFiles[0]
readUploadedFileAsText(file)
.then(res => {
setData({
data: res
})
})
}
return (
<div className="container">
<div {...getRootProps({style})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<File file={data} />
{console.log(data)}
</div>
);
}
export default Drag
Let me know if I have to use any lifecycle methods. I tried using componentDidMount and componentWillReceiveProps but both didn't work for me or I hadn't done them in right way.

Take your readUploadedFileAsText function outside of the if statement it is in. Then you can add your if and function call to React.useEffect which will call the function on initial load, but not on every subsequent re-render.
function Drag(props) {
const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, acceptedFiles } = useDropzone();
const style = useMemo(
() => ({
...baseStyle,
...(isDragActive ? activeStyle : {}),
...(isDragAccept ? acceptStyle : {}),
...(isDragReject ? rejectStyle : {})
}),
[isDragActive, isDragReject]
);
const [data, setData] = useState(null);
const readUploadedFileAsText = acceptedFiles => {
const temporaryFileReader = new FileReader();
return new Promise((resolve, reject) => {
temporaryFileReader.onerror = () => {
temporaryFileReader.abort();
reject(new DOMException('Problem parsing input file.'));
};
temporaryFileReader.onload = () => {
resolve(parser.fromSrt(temporaryFileReader.result));
};
temporaryFileReader.readAsText(acceptedFiles);
});
};
React.useEffect(() => {
if (acceptedFiles.length === 1) {
let file = acceptedFiles[0];
readUploadedFileAsText(file).then(res => {
setData({
data: res
});
});
}
}, [acceptedFiles]);
return (
<div className='container'>
<div {...getRootProps({ style })}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<File file={data} />
{console.log(data)}
</div>
);
}

Related

Adding sortInfo and/or filterValue to DataGrid breaks sorting/filtering functionality

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

React Native state does not update immediately when state is changed

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;

socket.io-client emmitting event multiple times in react native

I am new to using socket.io with react native.I am able to connect the socket.io instance with my node.js backend but the socket.io client is emmitting events multiple times.The app has grown quite complex so,I am also trying to explain what I did.
Main.js (User is redirected here from App.js)
I am using a library called react-native-tab-view and I used useContext to pass the socket instance.I have checked that the hook is working properly.
export const SocketObj = createContext()
const Main = ({ route }) => {
let [socket, setSocket] = useState(undefined)
const routesLength = useNavigationState(state => state.routes.length);
const connect = async () => {
if (routesLength == 1) {
setSocket(io("http://192.168.43.115:8000", {
transports: ['websocket'],
query: {
token: await AsyncStorage.getItem("token")
}
}))
} else {
const { socketInstanse } = route.params
setSocket(socketInstanse)
}
}
connect()
const layout = useWindowDimensions();
const [index, setIndex] = useState(0);
const [routes] = useState([
{ key: 'first', title: 'CHAT' },
{ key: 'second', title: 'PEOPLE' },
]);
const renderScene = SceneMap({
first: SplashScreen,
second: PeopleScreen,
});
return (
<SocketObj.Provider value={socket} >
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={(number) => {
setIndex(number)
}}
initialLayout={{ width: layout.width }}
/>
</SocketObj.Provider>
);
}
export default Main;
PeopleScreen.js
The user is directed here from Main.js. I have used the context api here to get the socket instance.
As you can see , i am using an event to log "Connected" to the console when connected but it is emitting multiple times.I don't want to emit it multiple times.Help me
import { SocketObj } from "./Main"
const PeopleScreen = ({ route, navigation }) => {
let socket = useContext(SocketObj)
socket.on("connect", () => console.log("Connect"))
const [peopleList, setPeople] = useState([])
useEffect(
() => {
const fetchData = async () => {
try {
const response = await API.get('get/users', {
headers: {
'Content-Type': 'application/json',
"auth-token": await AsyncStorage.getItem("token")
}
})
setPeople(response.data)
} catch (err) {
console.log(err.response)
}
}
fetchData()
}, []
)
return (
<View style={{
flex: 1,
backgroundColor: '#fff',
width: '100%',
height: '100%'
}} >
<View>
{
peopleList.map(
(i, key) => {
return (
<View style={{
display: 'flex',
flexDirection: 'row',
top: 40,
marginVertical: 5,
marginBottom: 10,
backgroundColor: 'grey',
height: 50
}}
key={key} >
<Text style={{
maxWidth: "50%"
}} >{i.firstName + ' ' + i.email}</Text>
<TouchableOpacity
onPress={
async () => {
console.log(socket)
API.post('post/add-friend', {
friend_email: i.email
}, {
headers: {
"auth-token": await AsyncStorage.getItem("token")
}
}).then(
data => console.log(data.data)
)
}
}
style={{
backgroundColor: 'rgb(255, 105, 105)',
width: 130,
justifyContent: 'center',
alignItems: 'center',
left: 150
}}
activeOpacity={0.6} >
<Text style={{
color: 'white',
}} >Add Friend</Text>
</TouchableOpacity>
</View>
)
}
)
}
</View>
</View >
)
}
export default PeopleScreen
I have ignored unnecessary imports.
Try to put your connect() inside an useEffect with an empty array as second argument of useEffect, then your connect function will be called only at the first render of Main.js.
...
const connect = async () => {
if (routesLength == 1) {
setSocket(io("http://192.168.43.115:8000", {
transports: ['websocket'],
query: {
token: await AsyncStorage.getItem("token")
}
}))
} else {
const { socketInstanse } = route.params
setSocket(socketInstanse)
}
}
useEffect(() => {
connect();
}, []);
...
if (!socket) return null; // or you can return a loading page
return (
<SocketObj.Provider value={socket} >
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={(number) => {
setIndex(number)
}}
initialLayout={{ width: layout.width }}
/>
</SocketObj.Provider>
);
It happens due to initialisation of socket multiple time.Put instance in Main.js .
If socket instance is empty then initialise socket.
{ useEffect(() => { if(!isConnected){ connect() } });

How to send file from react-dropzone to other api

I need to send a file from react-dropzone to a variable in a .js file. I wrote this code for dropzone:
const baseStyle = {
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '20px',
borderWidth: 2,
borderRadius: 2,
borderColor: '#eeeeee',
borderStyle: 'dashed',
backgroundColor: '#fafafa',
color: '#bdbdbd',
outline: 'none',
transition: 'border .24s ease-in-out'
};
const activeStyle = {
borderColor: '#2196f3'
};
const acceptStyle = {
borderColor: '#00e676'
};
const rejectStyle = {
borderColor: '#ff1744'
};
const maxSize = 5000000 // bytes
const acceptedFileTypes = '.csv, text/csv, application/csv, text/x-csv, application/x-csv, text/comma-separated-values, text/x-comma-separated-values'
const TFApartCsvInput = (props) => {
const [files, setFiles] = useState([]);
const {
getRootProps,
getInputProps,
isDragActive,
isDragAccept,
isDragReject,
acceptedFiles,
} = useDropzone({
accept: acceptedFileTypes,
multiple: false,
minSize: 0,
maxSize: maxSize,
onDrop: (acceptedFiles, rejectedFiles) => {
console.log('Accepted files:', acceptedFiles);
console.log('Rejected files:', rejectedFiles);
},
onDropAccepted: props.onCSVUpload,
onDropRejected: props.CSVTooBig
});
const style = useMemo(() => ({
...baseStyle,
...(isDragActive ? activeStyle : {}),
...(isDragAccept ? acceptStyle : {}),
...(isDragReject ? rejectStyle : {})
}), [
isDragActive,
isDragReject,
isDragAccept
]);
//ACCEPTED
const acceptedFileItems = acceptedFiles.map(file => (
<li key={file.path}>
{file.path}
</li>
));
// RETURN
return (
<div className="container">
<div {...getRootProps({style})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
<aside>
<h4>Drag 'n' drop here .CSV files</h4>
<ul>{acceptedFileItems}</ul>
</aside>
</div>
</div>
);
}
export default TFApartCsvInput;
and i have my import.js file which have a variable like this one:
let app_stream = fs.createReadStream("app.csv");
let csvData_app = [];
let csvData = [];
//other code
I wanna insert the file that the user drop in dropzone inside my app_stream variable of import.js. I can see from console that file is taken but I can't undestand how to manage it. Sorry if it sounds stupid, this is my first react project.
Your import.js will need to export a class or function, which will need to be imported from your Dropzoned component:
import.js
export default function (filename) {
// `fs` is a NodeJS library not suited for browsers. Are you sure?
let app_stream = fs.createReadStream(filename);
let csvData_app = [];
let csvData = [];
//other code
}
dropzoned.jsx
import Importer from './import.js'
// ...
onDropAccepted: (...args) => {
Importer(args); // I don't know what `args` contains
props.onCSVUpload(...args);
},
// ...

I can not send the text to firebase when Button is click with redux

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...)

Categories