I have created an array of dictionaries that hold the data of audios. The user must record those audios and then they can listen it. the problem is that those audios are represented with a flatlist and when I pass between them, it cannot find the uri. For example:
const [data, setData] = useState(
{uri: null, duration: 0},
{uri: null, duration: 0},
{uri: null, duration: 0}
)
const [index, setIndex] = useState()
const [step, setStep] = useState(0)
const [recording, setRecoding] = useState()
const [sound, setSound] = useState()
const [curDuration, setCurduration] = useState(0)
const [isBuffering, setIsBuferring] = useState(false)
const leftArrow = async () => {
if (index >= 1) {
flatlist.current.scrollToIndex({ animated: true, index: index - 1 })
if (newAudio[index - 1].grabacion != null) {
await sound.unloadAsync()
loadAudio(index - 1)
}
setIndex(index - 1)
};
}
const rightArrow = async () => {
if (index < newAudio.length - 1) {
flatlist.current.scrollToIndex({ animated: true, index: index + 1 })
if (newAudio[index + 1].grabacion != null) {
await sound.unloadAsync()
loadAudio(index + 1)
}
setIndex(index + 1)
}
}
const loadAudio = async (idx) => {
const playbackInstance = new Audio.Sound()
const source = { uri: newAudio[idx].grabacion }
const status = { shouldPlay: isPlaying }
playbackInstance.setOnPlaybackStatusUpdate((status) => { setIsBuffering(status.isBuffering) })
await playbackInstance.loadAsync(source, status, false)
setSound(playbackInstance)
}
const startRecording = async () => {
try {
setStep(1)
console.log('Requesting permissions..');
await Audio.requestPermissionsAsync();
await Audio.setAudioModeAsync({
allowsRecordingIOS: false,
playsInSilentModeIOS: true,
playThroughEarpieceAndroid: false,
staysActiveInBackground: true,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DUCK_OTHERS,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS,
});
console.log('Starting recording..');
const recording = new Audio.Recording();
await recording.prepareToRecordAsync(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY);
await recording.startAsync();
setRecording(recording);
console.log('Recording started');
} catch (err) {
console.error('Failed to start recording', err);
}
}
const stopRecording = async () => {
console.log('Stopping recording..');
setRecording(undefined);
await recording.stopAndUnloadAsync();
const uri = recording.getURI();
// set the sound
console.log('Loading Sound');
const { sound } = await Audio.Sound.createAsync({ uri: uri });
setSound(sound);
//check the status
let status = await sound.getStatusAsync()
//save uri and duration
updateValues( status.durationMillis, status.uri);
}
const updateValues = (dur, uri) => {
data[index] = {
duration: dur,
grabacion: uri
}
setNewAudio(newAudio)
}
return (
<View>
<Flatlist
data={data}
horizontal={true}
initialScrollIndex={index}
pagingEnabled={true}
scrollEnabled={false}
initialScrollIndex={index}
showsHorizontalScrollIndicator={false}
keyExtractor={(item, index) => index.toString()}
renderItem={(itemData) => {
<View>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<TouchableOpacity onPress={leftArrow}>
<AntDesign name='arrowleft' />
</TouchableOpacity>
<TouchableOpacity onPress={rightArrow}>
<AntDesign name='arrowright' />
</TouchableOpacity>
</View>
{step == 0 ?
<TouchableOpacity onPress={startRecording()}>
<Fontisto name='mic' />
</TouchableOpacity >
: step == 1 ?
<TouchableOpacity onPress={stopRecording()}>
<MaterialIcons name='stop' />
</TouchableOpacity >
: <TouchableOpacity onPress={HanddlePlayStop()} >
{isPlaying ?
<Fontisto name='pause' />
:
<Fontisto name='play' />
}
</TouchableOpacity >
}
<Slider
disabled={isPlaying ? true : false}
value={curDuration}
minimumValue={0}
maximumValue={itemData.item.duration}
onSlidingComplete={async (value) => {
await sound.setPositionAsync(value)
}}
/>
</View>
}
/>
</View>
)
The problem that I get is that when I press arrowRight() or arrowLeft() and if the index that we are going to there is already a recording, it does not identify the uri. Can somebody help me with this
I was able to solve it by saving the uri on a UseState and in the updateValues function, I'd change the value grabacion with the useState varable of the uri instead of status.uri. For some reason status.uri was not saving the uri correctly
Related
I'm new in redux, I'm using react native hooks and redux. my problem is that after opening the articleScreen.js page then clicking the back button and opening the articleScreen.js page again, the data is rendered again so that there is the same data and display the same data repeatedly when clicked on the articleScreen.js page. is there something wrong with my code below?.
=> ArticleScreen.js
const ArticleScreen = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.articleReducer.data);
const isLoading = useSelector((state) => state.articleReducer.isLoading);
const [loadingMore, setLoadingMore] = useState(false);
const [page, setPage] = useState(1);
const currentPage = useSelector((state) => state.articleReducer.currentPage);
const totalPage = useSelector((state) => state.articleReducer.totalPage);
const nextPage = useSelector((state) => state.articleReducer.nextPage);
useEffect(() => {
dispatch(fetchingArticle({ page: 1 }));
}, [])
const _renderItem = ({ item, index }) => {
return (
<TouchableOpacity
key={`${item.id} ${page}`}
style={{ marginBottom: 16 }}
>
<View style={{ flexDirection: 'row' }}>
<View style={{ flex: 1 }}>
<Text>{item.title}</Text>
</View>
</View>
</TouchableOpacity>
);
};
const handleLoadMoreData = () => {
if (!isLoading) {
setLoadingMore(true)
if (nextPage <= totalPage) {
dispatch(fetchingArticle(nextPage));
} else {
setLoadingMore(false)
}
}
}
return (
<>
<View>
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={item => `${item.id}`}
onEndReached={handleLoadMoreData}
onEndReachedThreshold={0.01}
ListFooterComponent={
loadingMore && (
<View
style={{
marginVertical: 30,
}}>
<ActivityIndicator
size="large"
color={Colors.onSurface}
/>
</View>
)
}
/>
</View>
</>
);
};
export default ArticleScreen;
=> store.js
const middleware = applyMiddleware(thunk);
// Root Reducer
const rootReducer = combineReducers({
articleReducer: ArticleReducer,
});
// Redux Store
const store = createStore(
rootReducer,
middleware
)
export default store;
=> ArticleAction.js
export const fetchArticleRequest = () => ({
type: 'FETCH_ARTICLE_REQUEST',
});
export const fetchArticleSuccess = (data) => ({
type: 'FETCH_ARTICLE_SUCCESS',
payload: data.data
});
export const fetchArticleFailure = (error) => ({
type: 'FETCH_ARTICLE_FAILURE',
payload: error
});
export const fetchingArticle = (page) => {
return async dispatch => {
dispatch(fetchArticleRequest());
return apiRequest.get(URL.articles + '?page=' + page).then((response) => {
dispatch(fetchArticleSuccess(response.data));
})
.catch((error) => {
dispatch(fetchArticleFailure("Request Data Error"))
})
}
}
=> ArticleReducer.js
const initialState = {
data: [],
error: '',
isLoading: false,
refreshing: false,
currentPage: 1,
totalPage: 1,
nextPage: 0,
totalData: 0
};
const ArticleReducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_ARTICLE_REQUEST':
return {
...state,
isLoading: true,
refreshing: true,
};
case 'FETCH_ARTICLE_SUCCESS':
return {
...state,
data: state.data.concat(action.payload.data),
isLoading: false,
refreshing: false,
currentPage: action.payload.current_page,
totalPage: action.payload.last_page,
nextPage: action.payload.current_page + 1,
};
case 'FETCH_ARTICLE_FAILURE':
return {
...state,
error: action.error,
isLoading: false,
refreshing: false,
};
default:
return state;
}
};
export { ArticleReducer };
I do not know Redux but maybe you need to load your data only one time and not every time you call your page unless you clear that you have before fetching them.
I will let other people who know better that me redux (I can't comment so do not count this answer as answer!)
The problem is with you reducer. You are concating previous data with new data
as
data: state.data.concat(action.payload.data)
In case 'FETCH_ARTICLE_SUCCESS': Change above line to
data: action.payload.data
Like
case 'FETCH_ARTICLE_SUCCESS':
return {
...state,
data: action.payload.data,
isLoading: false,
refreshing: false,
currentPage: action.payload.current_page,
totalPage: action.payload.last_page,
nextPage: action.payload.current_page + 1,
};
I can't get the datetimepicker to only open on the selected time. It seems to render the 3 available inputs that are available. I'm unsure how to deal with the showStart section where the datetimepicker window appears.
I would like to allow it to only show the relevant datetimepicker for the time selected when time is picked.
This is the current result.
Here is my code.
const renderItem = ({ item }) => {
const onChangeStart = (event, selectedTime) => {
setStartTime1(selectedTime);
var time2 = moment(selectedTime, "HH:mm").format("HH:mm");
updateStartTime(time2, iD);
console.log("Update Start Time start",time2, iD);
if (Platform.OS === "android") {
setShowStart(false);
}
};
const showTimepickerStart = () => {
setId(item.id);
showModeStart("time");
};
const showModeStart = (currentMode) => {
setShowStart(true);
setMode(currentMode);
};
const updateStartTime = (startTime, iD) => {
console.log("Update Start Time",startTime, iD);
dispatch(updateSchedules1(scheduleData.params.data.id, startTime, iD));
};
const onChangeEnd = (event, selectedTime1) => {
setEndTime1(selectedTime1);
var endTime = format(selectedTime1, "HH:mm");
updateEndTime(endTime, iD);
//console.log(endTime);
if (Platform.OS === "android") {
setShowEnd(false);
}
};
const showTimepickerEnd = () => {
setId(item.id);`enter code here`
showModeEnd("time");
};
const showModeEnd = (currentMode) => {
setShowEnd(true);
setMode(currentMode);
};
const updateEndTime = (endTime, iD) => {
console.log(endTime, iD);
dispatch(updateSchedules(scheduleData.params.data.id, endTime, iD));
};
return (
<View style={styles.deviceList}>
<StatusBar barStyle="light-content" backgroundColor="#0E67B4" />
<TouchableOpacity>
<CircularProgress
style={styles.iconProgress}
value={item.tilt_angle}
radius={34}
duration={1000}
textColor={"#0E67B4"}
titleFontSize={12}
maxValue={90}
title={"Tilt ยบ"}
titleColor={"#0E67B4"}
titleStyle={{ fontWeight: "bold" }}
/>
</TouchableOpacity>
<TouchableOpacity onPress={showTimepickerStart}>
<Text style={styles.time}>{item.start}</Text>
</TouchableOpacity>
{showStart && (
<DateTimePickerAndroid
testID="dateTimePicker"
value={new Date(2018, 12, 31)}
mode={mode}
is24Hour={true}
display="spinner"
onChange={onChangeStart}
/>
)}
im trying to get repos in order of stars from the API, but as i scroll, i keep getting random duplicates :
export default function App() {
const [repos, setRepos] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
useEffect(() => {
const getUsers = async () => {
const res = await axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`
);
const data = await res.data;
// setRepos([...repos, ...data.items]);
setRepos([...repos, ...data.items]);
setLoading(false);
};
getUsers();
}, [page]);
const scrollToEnd = () => {
if (loading === false) {
setPage(page + 1);
setLoading(true);
}
console.log("page", page);
};
const renderItem = ({ item }) => (
<Text className="boxed">
Stars: {item.stargazers_count} Id: {item.id}
</Text>
);
return (
<SafeAreaView style={styles.screen}>
<View style={styles.container}>
<FlatList
data={repos}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReached={scrollToEnd}
showsVerticalScrollIndicator={false}
/>
{loading && <Text className="loading">...loading</Text>}
</View>
<StatusBar style="auto" />
</SafeAreaView>
);
}
You should avoid duplicate data before set it to state, update your getUsers function with this,
useEffect(() => {
const getUsers = async () => {
const res = await Axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`,
);
const data = await res.data;
let arrRepos = [...repos, ...data.items];
let uniq = new Set(arrRepos.map((objRepo) => JSON.stringify(objRepo)));
arrRepos = Array.from(uniq).map((objRepo) => JSON.parse(objRepo));
setRepos(arrRepos);
setLoading(false);
};
getUsers();
}, [page]);
I think you got too much going on in your useEffect function, maybe missing a closing }. Try this:
function App() {
const [repos, setRepos] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
// This will trigger when page changes:
useEffect(() => {
getUsers();
}, [page]);
const getUsers = async () => {
const res = await axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`
);
const data = await res.data;
// setRepos([...repos, ...data.items]);
setRepos([...repos, ...data.items]);
setLoading(false);
};
const scrollToEnd = () => {
if (loading === false) {
setPage(page + 1);
setLoading(true);
}
console.log("page", page);
};
const renderItem = ({ item }) => (
<Text className="boxed">
Stars: {item.stargazers_count} Id: {item.id}
</Text>
);
return (
<SafeAreaView style={styles.screen}>
<View style={styles.container}>
<FlatList
data={repos}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReached={scrollToEnd}
showsVerticalScrollIndicator={false}
/>
{loading && <Text className="loading">...loading</Text>}
</View>
<StatusBar style="auto" />
</SafeAreaView>
);
}
export default App;
I will need a helping hand to edit this page. i have all the elements but i need help styling.
I would like to have the camera (the image you see is the typical emulator camera, that's why it makes an image) in full screen and from above at the top, the message in red and the 'autocomplete.
If you want, to explain better, I would like to respect the image below: autocomplete at the top left above the camera in full screen.
would it be possible for you to help me, I'm getting a little confused. I tried to do a snack but failed. I will add it later if i can.
const autocompletes = [...Array(10).keys()];
const apiUrl = "https://5b927fd14c818e001456e967.mockapi.io/branches";
class Tickets extends Component {
constructor(props) {
super(props);
this.state = {
Press: false,
hasCameraPermission: null,
reference: '',
lastScannedUrl:null,
displayArray: []
};
}
initListData = async () => {
let list = await getProductByRef(1);
if (list) {
this.setState({
displayArray: list,
reference: list.reference
});
}
// console.log('reference dans initListData =', list.reference)
};
async UNSAFE_componentWillMount() {
this.initListData();
// console.log('reference dans le state =', this.state.reference)
};
componentDidMount() {
this.getPermissionsAsync();
}
getPermissionsAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === "granted" });
};
_onPress_Scan = () => {
this.setState({
Press: true
});
}
handleBarCodeScanned = ({ type, data }) => {
this.setState({ Press: false, scanned: true, reference: data });
this.props.navigation.navigate('ProductDetails', {reference : parseInt(this.state.state.reference)})
};
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
if (hasCameraPermission === null) {
return <Text>{i18n.t("scan.request")}</Text>;
}
if (hasCameraPermission === false) {
return <Text>{i18n.t("scan.noaccess")}</Text>;
}
return (
<View
style={{
flex: 1,
...StyleSheet.absoluteFillObject
}}
>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1, height:'100%', ...StyleSheet.absoluteFillObject}}
/>
{scanned && (
<Button
title={"Tap to Scan Again"}
onPress={() => this.setState({ scanned: false })}
/>
)}
</View>
);
}
handleSelectItem(item, index) {
const {onDropdownClose} = this.props;
onDropdownClose();
console.log(item);
}
render() {
const { hasCameraPermission, scanned, Press } = this.state;
let marker = null;
const {scrollToInput, onDropdownClose, onDropdownShow} = this.props;
// console.log('displayArray', this.state.displayArray, 'reference', this.state.displayArray.reference)
return (
<View style={styles.container}>
{Press ? (
<View style={{flex:1}}>
<View style={styles.dropdownContainerStyle}>
<Autocomplete
key={shortid.generate()}
containerStyle={styles.autocompleteContainer}
inputStyle={{ borderWidth: 1, borderColor: '#F78400'}}
placeholder={i18n.t("tickets.warning")}
pickerStyle={styles.autocompletePicker}
scrollStyle={styles.autocompleteScroll}
scrollToInput={ev => scrollToInput(ev)}
handleSelectItem={(item, id) => this.handleSelectItem(item, id)}
onDropdownClose={() => onDropdownClose()}
onDropdownShow={() => onDropdownShow()}
fetchDataUrl={apiUrl}
minimumCharactersCount={2}
highlightText
valueExtractor={item => item.name}
rightContent
rightTextExtractor={item => item.properties}
/>
</View>
{this.renderBarcodeReader()}
</View>
) : (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<Button
color="#F78400"
title={i18n.t("scan.scan")}
onPress={this._onPress_Scan}>
</Button>
</View>
)}
</View>
);
}
}
export default Tickets;
This gives me (after pressing the button) :
SNACK CODE TEST
I notice You are using a component from Expo called BarCodeScanner
There's a github issue open about the fact that this component is not possible to be styled for full screen: https://github.com/expo/expo/issues/5212
However one user proposes a good solution: replace BarCodeScanner with Camera and use barcodescannersettings
Here's a link for the answer on the gitHub issue: https://github.com/expo/expo/issues/5212#issuecomment-653478266
Your code should look something like:
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
[ ... ] // the rest of your code here
return (
<View
style={{
flex: 1,
...StyleSheet.absoluteFillObject
}}
>
<Camera
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1}}
barCodeScannerSettings={{
barCodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
}}
/>
</View>
);
}
I want to login to my app but when I first login it works correctly but once I logout from my app and again try to login I get the following error 'Can't perform a state update on an unmount component'. Even though second time it also enters in the app but with the error which should be not there. Only one time it works correctly.
/*Component*/
const LoginScreen = props => {
let _isMounted = false;
const [isLoading , setIsLoading] = useState(false);
const [error , setError] = useState();
const [token , setToken] = useState();
const [url , setUrl] = useState({});
const dispatch = useDispatch();
/*Receiving the token*/
useEffect(() => {
let _isMounted = false;
const tokenReceive = () => {
if(Object.entries(url).length !== 0)
{
const getTokenFromUrl = url['url'].split('=')[1].split('&')[0];
if(getTokenFromUrl !== '')
{
setToken(getTokenFromUrl)
}
}
}
tokenReceive();
return(() => {
_isMounted = true
} )
}, [url ])
/*Dispatching after receiving token*/
useEffect(() =>{
_isMounted = true;
const loginHandler = async ()=> {
if(token !== undefined)
{
setError(null)
setIsLoading(true);
try{
await dispatch(authActions.login(token))
// if(_isMounted){
// props.navigation.navigate('afterAuth')
// }
}
catch(err)
{
setError(err.message)
}
setIsLoading(false)
if(_isMounted){
props.navigation.navigate('afterAuth')
}
}
}
loginHandler()
return(() => {
_isMounted = false
} )
} , [token ])
/*If any error occur*/
useEffect(() => {
if (error) {
Alert.alert('An error occured',error,[{text : 'Okay'}]);
}
return(() => {
console.log('Error'),
error
})
} , [error])
/*Event listener when url changes*/
useEffect(() => {
Expo.Linking.addEventListener('url', (url) => {
setUrl(url);
})
return () => {
Expo.Linking.removeEventListener('url' , (url) => {
setUrl(url)
})
};
} , [])
const prefix = Expo.Linking.makeUrl('token');
const _handlePressButtonAsync = async () => {
let result = await WebBrowser.openBrowserAsync(`https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=**********&response_type=id_token&redirect_uri=${prefix}&scope=openid email profile&response_mode=fragment&state=*****&nonce=****`);
};
return(
<ScrollView >
<TouchableWithoutFeedback onPress={() => {Keyboard.dismiss()}} >
<View style={styles.screen}>
<CircleDiv style={styles.userlogoDiv}>
<View style={styles.userLogo}>
<AntDesign name="user" size={RFValue(39)} color='#4D4848'/>
</View>
</CircleDiv>
<BackgroundUpper style={styles.upperDiv}>
<LogoLong style={ {marginTop : RFValue(100)}}/>
</BackgroundUpper>
<BackgroundLower >
<ScrollView style={{ flex : 1 } } decelerationRate='fast' >
<KeyboardAvoidingView behavior='position' keyboardVerticalOffset={Dimensions.get('screen').height / RFValue(10)}>
<View style={styles.loginDiv}>
<View style={styles.headingDiv}>
<Text style={styles.heading}>LOGIN</Text>
</View>
<View style={styles.buttonDiv}>
<TouchableOpacity>
{!isLoading ? <Button
style={styles.button}
title='LOGIN'
color= '#00B49D'
//onPress = {navigate}
onPress={_handlePressButtonAsync}
/> : <ActivityIndicator size="small" color={Colors.GREEN}/>}
</TouchableOpacity>
</View>
<View style={styles.forgetDiv}>
<Text style={styles.forget}>Forget Password</Text>
</View>
</View>
</KeyboardAvoidingView>
</ScrollView>
</BackgroundLower>
</View>
</TouchableWithoutFeedback>
</ScrollView>
)
};
Error - Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function,