Ayan Ayan - 1 month ago 23
React JSX Question

Async Image Load with React and Redux

I am trying to create a simple message wall with a

<PostList />
container which displays a list of
<Post />
components.

{posts.map(function (post: any) {
return <Post key={post.postid} post={post} />;
})}


I pass in a single post to the
Post
component which has a
<Avatar />
component that displays the users profile_pic inside it otherwise it displays a spinner.

My question is how would I allow the components to display on screen and once the image is loaded replace the spinner with the retrieved image?

I currently have the following Reducers and Actions:

User Reducer:

export default function(state = INITIAL_STATE, action : any){
switch(action.type){
case FETCH_USER_LOADING:
return Object.assign({}, state, {isLoading: true});
case FETCH_USER_DONE:
return Object.assign({}, state, {users: state.users.concat(action.payload)});
}

return state;
}


User Actions:

export function fetchUser(id: any) {
return function (dispatch: any) {
dispatch({ type: FETCH_USER_LOADING });
return axios.get(`${ROOT_URL}/users/${id}`, {
headers: { token: localStorage.getItem('token') }
})
.then(function (response) {
dispatch({type: FETCH_USER_DONE, payload: response.data});
return response.data
})
}
}

Answer

Plenty of ways to do it.

One of which is to write your own component, where, a newly loaded image prompts a redraw in componentDidMount. Here's the full source code of a lazy image that you can use in your own project:

export default class LazyImage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loaded: false,
      error: false
    };
  }

  componentDidMount() {
    const img = new Image();
    img.onload = () => {
      this.setState({
        loaded: true
      });
    };
    img.onerror = () => {
      this.setState({
        error: true
      });
    };
    img.src = this.props.src;
  }

  render() {
    if (this.state.error) {
      return <img
        className={this.props.className}
        style={this.props.style}
        src={this.props.unloadedSrc}
        alt={this.props.alt} />
    } else if (!this.state.loaded) {
      return <img
        className={this.props.className}
        style={this.props.style}
        src={this.props.unloadedSrc}
        alt={this.props.alt} />
    }
    return <img
      className={this.props.className}
      style={this.props.style}
      src={this.props.src}
      alt={this.props.alt} />
  }
}

Which you would then use like so:

<LazyImage unloadedSrc={unloadedSrc} src={src} />

And then, you have the option of using a plethora of components that you can find just by googling the terms:

  • "image load react"
  • "react image lazy load"

Or any similar search term variety, thereof. My favourite component is react-imageloader.

I hope this helps.