Skip to content

GoodPBC/meteor-react-image-search

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

#LEARNING METEOR AND REACT

Creating a React Application with Meteor.

###Project Name

--> .meteor

--> client (all code that is executed in the browser) \n

--> node_modules

--> .gitignore

--> package.json

###ReactJS Vs. Meteor

Meteor will Keep track of the the data in our application

###React takes our data and produces HTML with it. It also handles our user events

###A Meteor Project gives us: 1. a webdev server 2. A place to put our code 3. A place to track project dependencies

Install Meteor --MAC ONLY

Enter the following command in terminal --> "curl https://install.meteor.com/ | sh" wait for the installation to finish

linux and windows instructions

https://www.meteor.com/install

meteor folder

Should be left alone it holds all of the files that are specific to the meteor project.

Install React into your Project

"npm i --save react" You should now have a node_modules folder. this is where your dependencies are using the --save command will save the dependency to your pakcage.json file meteor-node-stubs dependency is given to us by default by meteor

Create a client folder

Delete the initial client folder and re-create it. In the folder create (touch) an index.html file

It is important to note that anything we place in the client folder will run when a client visits our server at port 3000

Get React into our project

we need to create a javascript file in the client folder. this we will call index.js

Test that your file works. "console.log" a hello world message in the file so that we can make sure we are talking to the client. you should see your message in the console

*We need to import react library into our project. es6 allows us to import full modules,parts of a module, functions, objects and more *

We will use an import statement "import React from 'react'"; We will also need an import statement for ReactDOM

We create a functional react component using es6's const *A functional react component is a not aware of its state. it is a basic / static component that is also known as a stateless component.

We render the component using ReactDOM.render, but we put it inside the Meteor.startup method in order to all the DOM to load first

###Congratulations you have just made a React component in Meteor.

Lets Move on to a bigger project

#React Image Voter Application We are going to create an image voter application React is all about understanding your data flow and planning the components carefully so that you can manage the application data successfully ##Challenges

  1. How to break up application into components

  2. We need to create a list of all the images to show

  3. We need to figure out how to get aLL of the individual images to produce one "image detail" for each image we have

LINK TO PLANNING

See The React Search UI MockUp

###Create a components folder in client for your React component.

Remember everything in client folder is what will be rendered we will then create our components

  1. image list
  2. image detail
  3. Voting

Remember, every time we create a React component we need to do 3 things

  1. import our libraries
  2. Create our component. either functional or class based *note --> remember *functional components are stateless, "unaware" of change around them.
  3. Export our components with the export default statement

###When we get down to the image detail component we need to start thinking about our data source.

Before building a dynamic data source we will build a static list of images to give our image detail component

###Building a static list of images for our application

In our image list file we are going to create a dummy list of images. each images is going to be an object that has a title and a link

create an array of objects for the images

use dummyimage.com to create placeholders

###Now we need to create a way for our images to each get into a separate ImageDetail component. This will happen in ImageList component.

It will be a two step process

  1. Write a variable inside the ImageList component for images. We make this variable equal to a function that uses the map method to iterate through our IMAGES object. Map, loops through
    const RenderedImages = IMAGES.map(function(image) {
      return <ImageDetail />
    });

    return (
      <ul>
        { RenderedImages }
      </ul>
    );
  };
  1. Instead of returning ImageDetail we replace it with RenderedImages because after we map over the IMAGES object RenderedImages becomes a new array which is an array of all of our ImageDetail components. This is how we solve the problem of rendering multiple components in a list.

###Using Props to transfer dat from image to ImageDetail

Here we will use props. Props is any data that is passed from a parent to a child. to pass props from parent to child we have to add it to the tag

    const RenderedImages = IMAGES.map(function(image) {
      return <ImageDetail image={image} />
    });

    return (
      <ul>
        { RenderedImages }
      </ul>
    );
  };

Notice that we add our image prop ("image={image}")into the ImageList component and then we pass the prop to the ImageDetail component by referencing our "prop.image.title". THIS IS HOW WE PASS DATA TO A COMPONENT

MORE ON props

for our image list we need to pass particular image from ImageList information to each component and render it in the image list. Here we use the "props.image.link" and use the prop as the img src.

By now you should have somtehing that looks like the react-image-searchUI-unstyled.jpg that is in this project. it will look even more basic than that as that is already slightly styled

Styling our components & the Meteor package manager.

In order to take advantage of frameworks we can pull them into tour meteor project using the packages that are available to install at https://atmospherejs.com/ . These packages are installed via the command line and all have documentation and github repos to help you work with and implement the packages.

To style our components we are going to use materialize, which is a responsive front end framework that is based on googles material design

we install it via the command line using the "meteor add materialize:materialize" command. it takes a moment

name we have materialize's front end framework available to us to begin to style our components

####In react we reference our components with "ClassName" instead of class. The reason for this is that in VanillaJS as of ecmaScript 2016, class is a reserved word in javascript.

We will take advantage of the predefined style rules that are available to the class's in materialize. We will use collection and collection-item to style our list and we can also use img classes as well. feel free to play around here

###Key props in lists

"Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of ImageList."

so RenderedImages is our array of ImageDetail components. Whenever we render an array of components in react,React expects that its going to have a key property available on each of the components in the array. This is strictly for performance. it makes it easier for react to render lists of items when the key property is available. We Need to add a prop to image detail to remove this warning.

To create a key prop you have assign a key that will be unique to each component in the array. Here we are going to use the title because, for our purposes, the title will be unique for each image in our list.

##BONUS

we are going to refactor our function for RenderedImages to make it a fat arrow function

  const RenderedImages = IMAGES.map(function(image) {
    return <ImageDetail key={image.title} image={image} />
});

can be refactored in two ways to be more current for es6

  1. We can make use of a fat arrow function and since there is only one argument (image) in our function we can remove the parenthesis around it:
       return <ImageDetail key={image.title} image={image} />
 });
  1. We can use an "implicit return". This mean that we don't have to use the return statement if we do not want to. When we have a single element our a single JS expression, we can use implicit returns. it is optional and is purely syntactic sugar. It looks like this:
       <ImageDetail key={image.title} image={image} />
   );

we remove the return keyword and the curly braces

###Getting dynamic images into our application using the Imgur API

this is our endpoint that we will use from IMGUR's API https://api.imgur.com/3/gallery/hot/viral/0.json

we need to install the axios package from npm in order to make AJAX requests npm install axios --save

*To test the response we will make our axios request from the app.js file which is fine for testing purposes *

Now that we have a response object we have to dial in on the information that we want to render in our app

##Introducing state in react. whenever we are working with data insode of react we want our components to be able to source/fetch its own data a render that data to the screen

Here we introduce a Class based component. Class comes from es6's Javascript Classes. Lets look at how we refactor a functional component to a class based component ####functional app component

  return (
    <div>
      <ImageList />
    </div>
  )
}

####Class app component

    render(){
      return (
        <div>
          <ImageList />
        </div>
      )
    }  
   };

As you can see above we remove the fat arrow function and replace it with a class statement and also create a render method which is executed whenever the component needs to be rendered to the screen We also need to destructure the React library and bring in our Component class from React by using a destructured import statement

###Loading data with lifecycle methods another method we have access to state aware components in react is componentWillMount "componentWillMount(){ console.log('ayyy')};"

This is a method that allows the component to be aware of when it will be rendered and do something before it renders. This make it a great place for our Axios call to happen. as soon as the "componentWillMount" method is called, the ajax call fires and we get our data. this presents a challenge though because the very next millisecond React will call the render method and the call may not be complete yet. In order to correct this we have to work with STATE

###Using state in React State is property that every class based component that we create has access to. state is a JS object that we can make changes to.When ever we change the state object the component will automatically re-render that component and all of its children

Lets figure out how to use state inside a React component

  1. The following app component is a class based component w/o state
    componentWillMount() { //great place to load data
      axios.get('https://api.imgur.com/3/gallery/hot/viral/0')
      .then(response => console.log(response));
    }

    render() {
      return (
        <div>
          <ImageList />
        </div>
      );
    }
  };```
  1. The is a class based component that takes advantage of state. Notice we ad a constructor method to our class. The constructor method is special method available to us in the JS class. when ever a class is instantiated the constructor method is automatically called. when it is called in React it will be called with the props object. exact same props that is passed from component to component. The component class that we are extending from which is implemented by React, has its own special implementation and configuration that it does inside of its own constructor. We call this parent constructor by calling "super(props)" --> not something we need to worry about to much

"this.state = { some: object};" initializes our state. our state object can have multiple properties. here we only have one. we always initialize our state to some reasonable value. Here we can expect that our state will be an array that is a response from our axios. In this case the response from the serveris going to be an array of images. That is why we init state as an empty array. When our request gets resolved it will now have some data. We only ever make reference to our state by saying "this.state ={key: value}". The reason we are using state is to forcibly re-render our component and all of its children when our request has completed.

  constructor(props) {
    super(props)

    this.state = { images: [] };
  }
  componentWillMount() { //great place to load data
    axios.get('https://api.imgur.com/3/gallery/hot/viral/0')
    .then(response => this.render()));
  }

  render() {
    console.log(this.state.images);
    return (
      <div>
        <ImageList />
      </div>
    );
  }
};

We will do some debugging by console.log 'ing our state in our render method - what happens? --> we should get back 2 empty arrays. one from the this.render that we should not be using and one from our console.log(this.state.images)

###Making state work

  axios.get('https://api.imgur.com/3/gallery/hot/viral/0')
  .then(response => this.setState({ images: response.data.data }));

TO get a response back from our API that has data in it we will replace this.render in the response we call "this.setState" of images to the the response body of our axios API call which is response.data.data....Now we should be getting two renders one empty and one that has called with setState and has a list of images.

The only time we ever use an "=" to determine state is when we are instantiating it inside of the constructor function. The rest of the times we use the setState method to update state.

###Passing Images as props We are still using a static list of images but our app.js is grabbing images dynamically from our API. So know we need to find a way to pass that data from our "parent" App component to our "child" the ImageList. whenever we want to talk from parent to child we use props. WHENEVER WE WANT TO PASS DATA FROM A PARENT TO A CHILD WE WILL ADD IT AS A PROP TO THE CHILD TAG In this case "images={this.state.images}" will go inside our imageList tag in the App component.like so:

render() { return(

<ImageList images={ this.state.images } />

);

Now our ImageList is being passed a prop called images and thte data is coming from this.state.images.

###Note that we instantiated our images state to be an empty array. That means the images prop when it shows up to imageList is always going to be an array. that means we can perform operations on it like map, length etc...

Lets clean up imageList Pre CleanUp:

    const RenderedImages = IMAGES.map(image =>
      <ImageDetail key={image.title} image={image} />
    );

    return (
        <ul>
          {RenderedImages}
        </ul>  
    );
};

Post CleanUp:

    const RenderedImages = props.images.map(image =>
      <ImageDetail key={image.title} image={image} />
    );

    return (
        <ul>
          {RenderedImages}
        </ul>  
    );
};

###What we did to refactor ImageList Instead of const images we want to grab the data on the props object that is given to us in every functional component by React. lets update and see what happens

###Okay so we have some broken images We successfully got our images to render but we go a lot of broken images

  1. go to console and open Network tab.
  2. navigate to XHR which will show us any ajax requests
  3. click the 0 line -this is our ajax requests
  4. click preview tab to find the data that we got from our request.
  5. find the broken link is_album: true

we need to refactor our what we are showing in our response to get rid of albums. so we need to create a filter. We need to create a new variable that will do some preprocessing for us

Lets clean up imageList Pre CleanUp:

    const RenderedImages = props.images.map(image =>
      <ImageDetail key={image.title} image={image} />
    );

    return (
        <ul>
          {RenderedImages}
        </ul>  
    );
};

Post CleanUp:

    const validImages = props.images.filter(image => !image.is_album);
    const RenderedImages = validImages.map(image =>
      <ImageDetail key={image.title} image={image} />
    );

    return (
        <ul>
          {RenderedImages}
        </ul>  
    );
};

so what we did was use the array filter method on our validImages const that we created. filter is just like map except if we return false on filter so for each image we do not want to include it if it is an album. Instead of mapping of

###Now we will move on to getting our image description into the Application We need to add the description IN THE ImageDetail Component. you will notice that reference to description is not in every image that is because not every image has a description we reference description in ImageDetail by accessing "props.image.descrition" remember the ImageDetail component creates one image rendered per instance. Image List creates a n array of imageDetail components.

###Lets move on to creating an image Score

we create our ImageVotes component with our component boiler plate.

About

A meteor and react image search engine

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published