I have started working with redux in react-native and I encountered problem, when I am dispatching
action in onLoadStart
function for Image component, the callback is firing and I see that action is called and I see Loading...
on screen, but when I am dispatching
in onLoadEnd
function nothing happen (callback loadEnd is not fired, app looks like frozen)
. When I use console.log in both places code is working fine. Could someone tell me what am I doing wrong?
class DisplayImage extends Component {
loadStart() {
this.props.iLoadingFunction(true);
}
loadEnd() { // <- this function is not fired
this.props.iLoadingFunction(false);
}
render() {
if (this.props.isLoading) {
return <Text>Loading…</Text>;
}
return (
<View>
<Image
source={{uri: 'https://test.com/image'}}
onLoadStart={this.loadStart.bind(this)}
onLoadEnd={this.loadEnd.bind(this)}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
isLoading: state.isLoading
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ iLoadingFunction }, dispatch);
};
export default connect(mapStateToProps, mapDispatchToProps)(DisplayImage);
action and reducer
export function iLoadingFunction(bool) {
return {
type: 'IS_LOADING',
isLoading: bool
};
}
export function iLoadingFunction(state = false, action) {
switch (action.type) {
case 'IS_LOADING':
return action.isLoading;
default:
return state;
}
}
I think the issue is coming from the order that actions and renders are occurring in. Let's look at the render
function:
render() {
if (this.props.isLoading) {
return <Text>Loading…</Text>;
}
return (
<View>
<Image
source={{uri: 'https://test.com/image'}}
onLoadStart={this.loadStart.bind(this)}
onLoadEnd={this.loadEnd.bind(this)}
/>
</View>
);
}
The first time the component mounts this.props.isLoading
is false
, so the component renders
<View>
<Image
source={{uri: 'https://test.com/image'}}
onLoadStart={this.loadStart.bind(this)}
onLoadEnd={this.loadEnd.bind(this)}
/>
</View>
The Image
mounts, begins loading the image and dispatches this.props.iLoadingFunction(true)
.
At this point isLoading
updates in the store are triggers a re-render of the component. This time this.props.isLoading
is true so the component renders
<Text>Loading…</Text>
Now the original Image
has been removed from the hierarchy. I'm not actually sure if it continues to download the image, but the onLoadEnd
handler does not fire as the component expecting to handle it has been removed.
To get around this, I would leave the Image
in the hierarchy and just include or exclude the Text
based on this.props.isLoading
, like so
render() {
return (
<View>
{ this.props.isLoading && <Text>Loading…</Text> }
<Image
source={{uri: 'https://test.com/image'}}
onLoadStart={this.loadStart.bind(this)}
onLoadEnd={this.loadEnd.bind(this)}
/>
</View>
);
}
The best solution to use callbackable-actions standardized actions. I recommend using the redux-cool package to do that
npm install redux-cool
import {actionsCreator} from "redux-cool"
const callback = () => {
console.log("Hello, I am callback!!!")
}
const callbackable_action = actionsCreator.CALLBACKABLE.EXAMPLE(1, 2, 3, callback)
console.log(callbackable_action)
// {
// type: "CALLBACKABLE/EXAMPLE",
// args: [1, 2, 3],
// cb: [[Function callback]],
// _index: 1
// }
callbackable_action.cb()
// "Hello, I am callback!!!"
for more info, see: Redux Cool
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.