DEV Community

Agbanusi John
Agbanusi John

Posted on

FreeCodeCamp take home project 2- Wikipedia previewer.

Okay, earlier I wrote about the take home project 1 which is a weather app, now we'll talk about the wikipedia previewer, let's shorten it to wiki previewer in this article.

Project: Wiki previewer,
Time taken: 4hrs,
Difficulty level: easy.
The link to the project is here

Please, it is advisable that you have gone through the entire freecodecamp curriculum before taking on the take home projects.

So our definition of this project is to create an app that when you type in a word, it brings up articles that matches the key word typed in the form. Also we would want to make our site responsive and pops up articles in real time, so we're typing, the articles popping up also changes to relevant ones.

So now to code! Let's start with the js part, I used react which is not bad but is a little overkill.

First:

class Main extends React.Component {
  constructor(props){
    super(props);
    this.state={
      value:'',
      result:[]
    }
  }
  render(){
   return(
    <div></div>
   )
 }
}

From the above we just defined a basic react component, and added some values to the state object, the value key would hold the input of the user, while the result will hold the results we get from the wikipedia api after sending a get request.

Next, we create a function and call it search, this function will send a get request and process the response gotten.

search(event,val){
    //console.log(this.state.value)
    let value= val? val: this.state.value
    event.preventDefault();
    fetch('https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info&inprop=url&utf8=&format=json&origin=*&srlimit=20&srsearch='+value).then(response=>response.json()).then(data=>{
      //console.log(data)
      this.setState({
        result:data.query.search
        })
    }).catch(err=>console.log(err))
  }

The search function above has two inputs, the event and the val. Reading through the function we'll see that the val input is temporary as if it doesn't see the val, it gets the value from the state object as written in line 2.
Someone might ask, why don't we just get directly from the state, instead of asking from an input of val. We'll answer that in the next section. The third line just makes it hard for the form to submit empty string to the fetch request, as the function is a form submit handler. We then made a fetch request to

'https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info&inprop=url&utf8=&format=json&origin=*&srlimit=20&srsearch='

and added the value with string concatenation and after getting the request in json, we put the required results we need in the state object. We saw before setting the state I visualised the data to know what I want to get exactly from it. The catch is to catch any errors encountered instead of breaking the app and print to console.

So the next function is

handleChange(e){
    this.setState({
      value:e.target.value
    })
    this.search(e,e.target.value)
  }

This is to handle change in input to the form, so as you type in the input form, it automatically saves the value in the state object. We also want to trigger the search function we defined above to show us the results after request to and response from the wiki page. But we would have a problem as it won't be in real time, it'll bring up results of one input ago. The reason is the state won't change until the component re-renders, but we're still in the handleChange function and the only value we'll have access to is the former one until it re-renders. We want to still see real time results, so what do we do? We can just pass the new value as a second input to the search function, remember we made provision for that, so the value variable becomes the val input. Also don't forget that the search is also a submit handler, the input val won't be inputted by default, so that's why we had a fallback getting the value from this.state.value. Note that on submit, the component automatically renders, so we'll be having the new correct value, win-win right? On change it shows results in real time, on submit also shows results in real time!

Okay, one more function to go...

clear(){
    this.setState({
      value:''
    })
  }

This function above is a simple function to clear the value of this.state.value, maybe to input another word or sentence.

Next is the render part,

render() {
    //console.log(this.state.result)
    var bee=''
    this.state.result.map((i,index)=>{
          let url='https://en.wikipedia.org/wiki/'+encodeURI(i.title)
          bee+= "<div class='card'><a href="+url+" target=_blank><h3>"+i.title+"</h3><hr><p>"+i.snippet+"</p></a></div>"
          })
    //console.log(bee)
    if(bee){document.getElementById('top').innerHTML=bee}
    return (
      <div className='body'>
        <h2> Wikipedia Previewer</h2>
        <div className='search'>
        <form onSubmit={this.search}>
          <input type='text' onChange={this.handleChange} value={this.state.value} />
          <button type='reset' className='x' onClick={this.clear}>x</button>
          <button type='submit'><i className="fas fa-search" /></button>
          <button><a href='https://en.wikipedia.org/wiki/Special:Random' target='_blank'><i className="fas fa-random"></i></a></button>
        </form>
        </div>
        <div id='top' />
      </div>
    );
  }

Okay, what do we have here! It's not one complicated thing. Let's go over it together.

On render, we created a bee variable to a string containing some html combinations and injecting it into the element with id named 'top'. Someone may say we could just put this in the search function, but remember whenever a function does a change of state, we can't access the new value until it re-renders. So another way to beat it is getting your objects in the render function, it will always trigger a re-render before executing what is inside it, but note it is outside the return statement. But whenever you're putting statements in the render, remember to NEVER set a state in it, it'll trigger an infinite loop and believe me you don't want that.
So inside the render, the mapped the array from this.state.results and created a url variable which uses the title key value to create a link element and also used the snippet key value to show a some text on the topic also displayed and concatenated them to the bee variable, and this is done for all the values of the result array. After this it is injected into the div with element with id 'top' using 'innerHTMl', and voila we're done!.

The return statement is used to create the needed elements for displaying and getting results, notice the event handler on the form element and input element? We should also make sure we render with react dom.

ReactDOM.render(<Main />, document.getElementById('root'));

My html looks like this:

<html>
    <head>
        <link rel="stylesheet" href="style.css">
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
    </head>
    <body>
        <div id="root"></div>
        <script src="index.js"></script>
    </body>
</html>

The link for fontawesome is for the icons I used in my project, that is the search and random icons.

You can test my app here below codepen. Note it may take a little time like 2-5 seconds to show results of input, this is due to the fetch request which sometimes is slow to return responses. Enjoy!

Top comments (0)