0

Hi I've been trying out a bit of react and electron and I'm just trying to make a media playlist type of application but I am having a bit of trouble with Redux

So I have setup the actions and they do kind of work but for some reason on the first initial load of a component the array.map that I am using to display all the results in a list won't actually render.

I have got the console out putting the results and when the component renders the first pass of the render fucntion the initial state is null then on the second pass the console logs the correct output but the array.map is still not outputting anything.

enter image description here

Then when I save a file in my editor (hot reload is on) I will then get an item to render on from the array.map

enter image description here

I can't work out if I have made astupid mistake somewhere or if I am just completly doing the wrong thing. So I'm hoping maybe someone might be able to shed some light on the situation.

Here is my component file with this array.map function that isn't working

interface Props {
  songs?: any;
  receiveMedia?: Function;
}

interface SongState {}

export default class Songs extends React.Component<Props, SongState> {
  private initLoad = 0;
  constructor(props: Props) {
    super(props);
    this.state = {};

    this.getThumbnailRender = this.getThumbnailRender.bind(this);
  }

  componentDidMount() {
    this.props.receiveMedia && this.props.receiveMedia();
  }

  getThumbnailRender() {
    console.log(this.props.songs);
    if (this.props.songs.Media.songs !== null) {
      return this.props.songs.Media.songs.map((media: any) => {
        if (media.extension !== "mp4") {
          return (
            <li
              className={css.thumbNail}
              id={media.id}
              key={`${"media_thumb_"}${media.id}`}
            >
              <img src={mp3} />
              <span className={css.floatingText}>{media.fileName}</span>
            </li>
          );
        } else {
          return (
            <li
              className={css.thumbNail}
              id={media.id}
              key={`${"media_thumb_"}${media.id}`}
            >
              <img src={media.filePath} />
              <span className={css.floatingText}>{media.fileName}</span>
            </li>
          );
        }
      });
    }
    return <div>You haven't added any songs</div>;
  }

  render() {
    return (
      <div className={css.container}>
        <h1>Songs</h1>
        <div className={css.songHolder}>
          <ul>{this.getThumbnailRender()}</ul>
        </div>
      </div>
    );
  }
}

I'm pretty sure that the Action and the reducer files are fine as they work later on but I will include them just incase I have made a stupid mistake

ACTIONS.ts

import { Songs } from "../../Models";
var fs = require("fs");

export enum ActionTypes {
  MEDIA_RECEIVED = "[Media] MEDIA_RECEIVED"
}

export interface MediaReceived {
  type: ActionTypes.MEDIA_RECEIVED;
  payload: {
    globals: Songs;
  };
}

export function mediaReceived(json: any): MediaReceived {
  return {
    type: ActionTypes.MEDIA_RECEIVED,
    payload: {
      globals: json
    }
  };
}

function loadInCurrentSongList() {
  var obj: Songs = {
    //@ts-ignore
    songs: []
  };
  fs.readFile("saveFile.json", "utf-8", (err: any, data: any) => {
    if (err) {
      alert("An error ocurred reading the file :" + err.message);
      return;
    }
    const newData = JSON.parse(data);
    if (newData.songs.length > 0) {
      newData.songs.map((song: any) => {
        obj.songs.push(song);
      });
    }
  });
  return obj;
}

export function receiveMedia() {
  return (dispatch: Function) => {
    dispatch(mediaReceived(loadInCurrentSongList()));
  };
}

export type Action = MediaReceived;

REDUCER.ts

import { Songs } from "../../Models";
import { Action, ActionTypes } from "./Actions";

export interface State {
  songs: Songs | null;
}

export const initialState: State = {
  songs: null
};

export function reducer(state: State = initialState, action: Action) {
  if (action.type === ActionTypes.MEDIA_RECEIVED) {
    return Object.assign({}, state, action.payload.globals);
  } else {
    return state;
  }
}

Thank you very much :)

1 Answer 1

1

Because fs.readFile is async and the callback passed to it is executed at a later time when the fs operation completes, obj is being returned from loadInCurrentSongList before it is populated with the songs, and therefore when mediaReceived is dispatched songs is still empty. Your debugger is fooling you a bit because it displays the updated value of obj after it gets populated in the fs.readFile callback.

The hot reload works because it forces a re-render without destroying state, at which point obj has been mutated inside of the fs.readFile callback.

Here's one option to manage the async nature of fs.readFile with a Promise such that you wait for it complete instead of mutating the obj returned from loanInCurrentSongList. Not super familiar with typescript so you'll have to update the types probably:

function loadInCurrentSongList() {
  return new Promise(resolve => {
    fs.readFile("saveFile.json", "utf-8", (err: any, data: any) => {
      var obj: Songs = {
        //@ts-ignore
        songs: []
      };
      if (err) {
        alert("An error ocurred reading the file :" + err.message);
        resolve(obj);
        return;
      }
      const newData = JSON.parse(data);
      if (newData.songs.length > 0) {
        newData.songs.map((song: any) => {
          obj.songs.push(song);
        });
      }
      resolve(obj);
    });
}

export function receiveMedia() {
  return (dispatch: Function) => {
    loadInCurrentSongList().then(songList => {
      dispatch(mediaReceived(songList));
    }
  };
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, and the answer that you have given has actually really helped me to understand what the problem was!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.