react-native why I get memory leak if I use this useEffect method - react-native

why I get memomy leak if I use this code:
useEffect(() => {
if(step === 2) {
BackHandler.addEventListener('hardwareBackPress', () => handleStep1WithBackhandler());
return () => {
BackHandler.removeEventListener('hardwareBackPress', () => handleStep1WithBackhandler());
}
} else {
if(hardware === true) {
BackHandler.addEventListener('hardwareBackPress', () => false);
return () => {
BackHandler.removeEventListener('hardwareBackPress', () => false);
}
}
}
}, [step]);
if step is equal to 2 then I go back to step1 with the function. Else nothing.
Whats wrong with that?

May be due to arrow functions in addEventListener and removeEventListener
In addition to the value of the step within the eventListener you can use this approach:
Create a customHook for tracking a state to a ref.
const useStateRef = (defaultValue = null)=> {
const [value, setValue] = useState(defaulValue)
const ref = useRef()
useEffect(()=> {
ref.current = value
},[value])
return [value, setValue, ref]
}
and use it as follows
const SomeComponent = () => {
const [step, setStep, stepRef] = useStateRef(1)
const handleBackPress = React.useCallBack(() => {
if (stepRef.current === 2) {
//some logic
}
if (someWhere.hardware) {
//some logic
}
},[])
useEffect(()=> {
BackHandler.addEventListener('hadwareBackPress',handleBackPress)
return ()=> BackHandler.removeEventListener('hadwareBackPress',handleBackPress)
},[])
//some code ...
//return something ...
return null
}

Related

how to reset navigation addListener when a state is changed

I want to reset navigation addListener when state is changed but It's not working
useEffect(() => {
const {page, pageIdx, tab} = pageInfo
const remove = navigation.addListener('state', ({data:{state:{routes, index}}}) => {
if(routes[index].name === name){
if(pageIdx)
getMatchItem(`usr/goods/match/buy/history/${tab}/${page}`)
else
getItem(`usr/goods/auction/bid/${tab}/${page}`)
}
return () => remove()
})
}, [pageInfo])
so I tried to return remove function when state is changed but It couldn't work
For example:
const [stateChanged, setStateChaged] = useState(false)
const [listener, setListener] = useState(null)
useEffect(() => {
if(stateChanged && listener) {
listener()
setListener(null)
}
}, [stateChanged])
useEffect(() => {
const {page, pageIdx, tab} = pageInfo
const remove = navigation.addListener('state', ({data: {state: {routes, index}}}) => {
if (routes[index].name === name) {
if (pageIdx)
getMatchItem(`usr/goods/match/buy/history/${tab}/${page}`)
else
getItem(`usr/goods/auction/bid/${tab}/${page}`)
setStateChaged(true)
}
})
setListener(remove)
}, [pageInfo])

asyncstorage with useReducer does not save the values

i am new to reactnative, i was trying to build todo app using useReducer and asyncstorage but not able to achieve that, i have read every article and related questions i am able to do it using useState but did not got the result with useReducer.
Any help/suggestions will be appreciated. Thank You .
const MainScreen = () => {
const getData = async () => {
try {
const data = await AsyncStorage.getItem('itemlist');
return (output = JSON.parse(data));
} catch (e) {
}
};
React.useEffect(() => {
getData();
}, []);
const [text, setText] = React.useState('');// for textinput
const { dispatch } = useContext(NotesContext);
const handleSubmit = async () => {
try {
const output = JSON.stringify(state);
await AsyncStorage.setItem('itemlist', output);
} catch (error) {
console.log(error);
}
if (text) {
dispatch({ type: 'ADD_TODO', payload: text });
}
setText('');
};
//this is my reducer function below
export const TodoReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return state.concat({
task: action.payload,
id: uuid.v4(),
complete: false,
});
case 'DELETE_TODO':
return state.filter((todo) => todo.id !== action.payload);
default:
throw new Error();
}
};

React Native how to export API response

how to export API response to another components?
i have axios that bring data from the back-end to the front-end, can i export the response data to another component/screen or i have to do Fetch/axios on each part i want render data on it
i have the API response on Home page, so i can export the data
Home.js
.....
const Home = ({ navigation }) => {
const [ChartData, setChartData] = useState([]);
const [BalanceData, setBalanceData] = useState([]);
const [ExpensesData, setExpensesData] = useState([]);
const [SavingsData, setSavingsData] = useState([]);
const [IncomeData, setIncomeData] = useState([]);
const [LoansData, setLoansData] = useState([]);
const getData = () => {
axios.get('http://192.168.8.143:8000/transactions/')
.then(function (response) {
// handle success
console.log(response);
setChartData(response.data);
let data = response.data;
let balance = data?.filter((vl) => {
return vl?.type == "BALANCE"
});
setBalanceData(balance)
let savings = data?.filter((vl) => {
return vl?.type == "SAVING"
})
setSavingsData(savings);
let loans = data?.filter((vl) => {
return vl?.type == "LOANS"
});
setLoansData(loans);
let income = data?.filter((vl) => {
return vl?.type == "INCOME"
});
setIncomeData(income);
let expenses = data?.filter((vl) => {
return vl?.type == "EXPENSES"
})
setExpensesData(expenses);
})
.catch(function (error) {
// handle error
console.log(error);
})
}
useEffect(() => {
getData();
}, [])
console.log("Chart Data", ChartData);
...
...
export default Home;
In this scenario it is usually a good idea to encapsulate your business logic in a custom hook which you can then reuse in every functional component. This could look as follows.
export const useMyBackendApi = () => {
const [ChartData, setChartData] = useState([]);
const [BalanceData, setBalanceData] = useState([]);
const [ExpensesData, setExpensesData] = useState([]);
const [SavingsData, setSavingsData] = useState([]);
const [IncomeData, setIncomeData] = useState([]);
const [LoansData, setLoansData] = useState([]);
const getData = () => {
axios.get('http://192.168.8.143:8000/transactions/')
.then(function (response) {
// handle success
console.log(response);
setChartData(response.data);
let data = response.data;
let balance = data?.filter((vl) => {
return vl?.type == "BALANCE"
});
setBalanceData(balance)
let savings = data?.filter((vl) => {
return vl?.type == "SAVING"
})
setSavingsData(savings);
let loans = data?.filter((vl) => {
return vl?.type == "LOANS"
});
setLoansData(loans);
let income = data?.filter((vl) => {
return vl?.type == "INCOME"
});
setIncomeData(income);
let expenses = data?.filter((vl) => {
return vl?.type == "EXPENSES"
})
setExpensesData(expenses);
})
.catch(function (error) {
// handle error
console.log(error);
})
}
useEffect(() => {
getData();
}, [])
return {
ChartData,
BalanceData,
ExpenseData,
SavingsData,
IncomeData,
LoansData,
}
}
The in your Screen, you can use your hook.
const Home = ({ navigation }) => {
const { ChartData, BalanceData } = useMyBackendApi() // destructure whatever you need from your hook
...
// do whatever
}
You can do the same in any other functional component.
It might be a good idea to cache your server response in order to prevent unwanted refetches. This could be done using SWR Hooks. This could be used as follows in your scenario.
import useSWR from 'swr'
export const useMyBackendApi = () => {
// first param is your cache key, second param your fetcher
const { data, error, mutate } = useSWR("http://192.168.8.143:8000/transactions/", (url) => {
return axios.get('http://192.168.8.143:8000/transactions/')
.then(function (response) {
let data = response.data;
let balance = data?.filter((vl) => {
return vl?.type == "BALANCE"
});
let savings = data?.filter((vl) => {
return vl?.type == "SAVING"
})
let loans = data?.filter((vl) => {
return vl?.type == "LOANS"
});
let income = data?.filter((vl) => {
return vl?.type == "INCOME"
});
let expenses = data?.filter((vl) => {
return vl?.type == "EXPENSES"
})
return {
chartData: response.data,
balanceData: balance,
savingsData: savings,
loansData: loans,
incomeData: income,
expensesData: expenses
}
})
})
const refresh = React.useCallback(() => {
return mutate()
}, [mutate])
return {
data,
refresh,
error
}
}
Your data is now cached using swr hooks and your buisness logic is encapsulated in a custom hook. You can reuse this in every screen as follows.
const Home = ({ navigation }) => {
const {data} = useMyBackendApi()
console.log(data.incomeData)
}
The procedure is the same in any other screen. Notice, that your data will not be refetched unless you trigger a mutate of your cache using the mutate function of swr. However, you could make the use of dependent cache keys for more complex situations.

How to use focus and blur listener in single useEffect react native

As you know in useEffect we return the unsubscribe at the end if we assign any listener to unsubscribe const as shown under
As we Using
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// code
})
return unsubscribe;
}, [navigation]);
As I want
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// code
})
const unsubscribe2 = navigation.addListener('blur', () => {
// code
})
// need to return both listeners
}, [navigation]);
You can cleanup like this
useEffect(() => {
navigation.addListener('focus', handler)
navigation.addListener('blur', handler)
return () => {
navigation.removeListener('focus', handler)
navigation.removeListener('blur', handler)
}
},[navigation])
The official example here https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup
I didn't test this, but you might be able to do something like this:
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// code
});
const unsubscribe2 = navigation.addListener('blur', () => {
// code
});
return () => {
// executed when unmount
unsubscribe();
unsubscribe2();
}
}, [navigation]);

Integrating Pull-To-Refresh in a ScrollView with Redux in React Native

I am trying to add pull-to-refresh functionality to a <ScrollView> using a refreshControl and integrate it with Redux.
Example from https://facebook.github.io/react-native/docs/refreshcontrol:
_onRefresh = () => {
this.setState({refreshing: true});
fetchData().then(() => {
this.setState({refreshing: false});
});
}
My problem is that my own fetchData function dispatches an action for the reducer to handle, so as far as I understand it, it is not a thenable promise. So I don't fully understand the integration with Redux in this case. What do I need to change in my code to be able to set refreshing to false as in the above example?
PostFeedScreen.js
// on mount, fetch all posts from the API
componentDidMount() {
this.props.fetchPostsFromAPI();
}
_onRefresh = () => {
this.setState( { refreshing: true } );
this.props.fetchPostsFromAPI().then( () => { // error
this.setState( { refreshing: false } );
});
}
// map dispatch to props
const mapDispatchToProps = ( dispatch ) => {
return {
fetchPostsFromAPI: () => {
dispatch( fetchPostsFromAPI() );
}
}
}
PostActions.js
// fetch all posts
export function fetchPostsFromAPI() {
return( dispatch ) => {
let loadData = new Promise( ( resolve, reject ) => {
resolve( postsInitial ); // using dummy data for now
})
loadData
.then( posts => dispatch( fetchPostsSuccess( posts ) ) );
}
// is used if posts were succesfully loaded
function fetchPostsSuccess( posts ) {
return {
type: PostConstants.FETCH_POSTS_SUCCESS,
data: posts,
}
}
PostReducer.js
const PostReducer = ( state = initialState, action ) => {
switch( action.type ) {
// if fetching data was successful
case PostConstants.FETCH_POSTS_SUCCESS: {
return {
...state,
posts: action.data,
}
}
default: {
return state
}
}
You get an error cause you call .then on something who don't return a promises. Just add return in front of your loadData, cause you can chain promises.
export function fetchPostsFromAPI() {
return dispatch => {
let loadData = new Promise((resolve, reject) => {
resolve(postsInitial);
});
return loadData.then(posts => dispatch(fetchPostsSuccess(posts)))
};
}