about error “npm ERR! code ELIFECYCLE”:

if meet this situation when npm start:
https://stackoverflow.com/questions/42308879/npm-err-code-elifecycle

  1. $ npm cache clean --force
  2. $ rm -rf node_modules package-lock.json
  3. $ npm install
  4. $ npm start

about err “System limit for number of file watchers reached”:

https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers#the-technical-details

  1. $ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
  2. $ sudo sysctl -p

React.js Action Btn:

This part is to make action buttons base on reactjs.

  • add LikeBtn() in App.js ```javascript //… function LikeBtn(props) { const {tweet} = props return “<button class=’btn btn-primary btn-sm’ onclick=handleTweetActionBtn(“
    1. + tweet.id + "," + tweet.likes + ",'like')>"
    2. + tweet.likes
    3. + " Likes</button>"
    }

function Tweet(props) { //… return

{tweet.id} - {tweet.content}

} //…

  1. both runserver and npm start, then access localhost:30<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1593321968942-1e0f23e0-fc41-43a4-8653-a519d8f1b6d8.png#align=left&display=inline&height=482&margin=%5Bobject%20Object%5D&name=image.png&originHeight=963&originWidth=1744&size=103373&status=done&style=none&width=872)<br />so we have the string of like button in the div.
  2. - a little modification on like button:
  3. ```javascript
  4. function LikeBtn(props) {
  5. const {tweet} = props
  6. /*
  7. return "<button class='btn btn-primary btn-sm' onclick=handleTweetActionBtn("
  8. + tweet.id + "," + tweet.likes + ",'like')>"
  9. + tweet.likes
  10. + " Likes</button>"
  11. */
  12. const className = props.className ? props.className : 'btn btn-primary btn-sm' //mew
  13. return <button className={className}>{tweet.likes} Likes</button> //new
  14. }

then see reactjs page
image.png
now we have buttons with out actual use.

  • extend like button to action button in App.js: ```javascript //function LikeBtn(props) { function ActionBtn(props) { //const {tweet} = props const {tweet, action} = props const className = props.className ? props.className : ‘btn btn-primary btn-sm’ //return return action.type === ‘like’ ? : null }

function Tweet(props) { //… return

{tweet.id} - {tweet.content}

}

  1. It shows the same thing on reactjs page as before.
  2. <a name="XT3da"></a>
  3. ### Using JavaScript Modules:
  4. - in order to increase code reuse, create a tweets/ folder in /twittme/src to save frequently used functions
  5. - create index.js and components.js in tweets/
  6. - **cut** 2 functions in App.js then paste into components.js and modify them
  7. ```javascript
  8. import React from 'react'
  9. export function ActionBtn(props) {
  10. const {tweet, action} = props
  11. const className = props.className ? props.className : 'btn btn-primary btn-sm'
  12. return action.type === 'like' ? <button className={className}>{tweet.likes} Likes</button> : null
  13. }
  14. export function Tweet(props) {
  15. const {tweet} = props
  16. const className = props.className ? props.className : 'col-10 mx-auto col-md-6'
  17. return <div className={className}>
  18. <p>{tweet.id} - {tweet.content}</p>
  19. <div className='btn btn-group'>
  20. <ActionBtn tweet={tweet} action={{type: "like"}}/>
  21. <ActionBtn tweet={tweet} action={{type: "unlike"}}/>
  22. </div>
  23. </div>
  24. }
  • import and export 2 functions in index.js ```javascript import {ActionBtn, Tweet} from ‘./components’

export { ActionBtn, Tweet }

  1. - create TweetsList() in components.js and **cut** loadTweets() in App.js to components.js
  2. ```javascript
  3. //import React from 'react'
  4. import React, {useEffect, useState} from 'react' //new
  5. function loadTweets(callback){
  6. const xhr = new XMLHttpRequest()
  7. const method = 'GET'
  8. const url = "http://localhost:80/api/tweets/"
  9. const responseType = 'json'
  10. xhr.responseType = responseType
  11. xhr.open(method, url)
  12. xhr.onload = function() {
  13. callback(xhr.response, xhr.status)
  14. }
  15. xhr.onerror = function (e) {
  16. console.log(e)
  17. callback({"message": "The request was an error"}, 400) //when status 400, callback "The request was an error"
  18. }
  19. xhr.send()
  20. }
  21. export function TweetsList(props) {
  22. const [tweets, setTweets] = useState([])
  23. useEffect(() => {
  24. const myCallback = (response, status) => {
  25. if (status === 200){
  26. setTweets(response)
  27. } else { //if status not 200, alert "There was an error"
  28. alert("There was an error")
  29. }
  30. }
  31. loadTweets(myCallback)
  32. }, [])
  33. return tweets.map((item, index)=>{
  34. return <Tweet tweet={item} className='my-5 py-5 border bg-white text-dark' key={`${index}-{item.id}`} />
  35. })
  36. }
  37. export function ActionBtn(props) {
  38. const {tweet, action} = props
  39. const className = props.className ? props.className : 'btn btn-primary btn-sm'
  40. return action.type === 'like' ? <button className={className}>{tweet.likes} Likes</button> : null
  41. }
  42. export function Tweet(props) {
  43. const {tweet} = props
  44. const className = props.className ? props.className : 'col-10 mx-auto col-md-6'
  45. return <div className={className}>
  46. <p>{tweet.id} - {tweet.content}</p>
  47. <div className='btn btn-group'>
  48. <ActionBtn tweet={tweet} action={{type: "like"}}/>
  49. <ActionBtn tweet={tweet} action={{type: "unlike"}}/>
  50. </div>
  51. </div>
  52. }
  • modify App.js to apply the js module ```javascript //import React, {useEffect, useState} from ‘react’ import React from ‘react’; import logo from ‘./logo.svg’; import ‘./App.css’;

import {TweetsList} from ‘./tweets’

//loadTweets(), ActionBtn() and Tweets are moved to components.js

function App() { /* const [tweets, setTweets] = useState([])

useEffect(() => { const myCallback = (response, status) => { if (status === 200){ setTweets(response) } else { //if status not 200, alert “There was an error” alert(“There was an error”) } } loadTweets(myCallback) }, []) */

return (

logo

Edit src/App.js and save to reload.

  1. <div>
  2. <TweetsList />
  3. </div>
  4. <a
  5. className="App-link"
  6. href="https://reactjs.org"
  7. target="_blank"
  8. rel="noopener noreferrer"
  9. >
  10. Learn React
  11. </a>
  12. </header>
  13. </div>

); }

export default App;

  1. refresh to test<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1593402250142-af6e7a65-e5ff-4613-b926-9aa9673584b9.png#align=left&display=inline&height=435&margin=%5Bobject%20Object%5D&name=image.png&originHeight=869&originWidth=1803&size=113409&status=done&style=none&width=901.5)<br />so that the module is successfully applied, there are no differences between 2 versions.
  2. - to make another module and separate functions, create lookup/ folder in /twittme/src to move loadTweets() inside
  3. - and 2 corresponding files: index.js and lookup.js in lookup/
  4. - **cut** loadTweets() in /twittme/src/tweets/components.js and paste in /twittme/src/lookup/lookup.js
  5. ```javascript
  6. export function loadTweets(callback) {
  7. const xhr = new XMLHttpRequest()
  8. const method = 'GET' // "POST"
  9. const url = "http://localhost:8000/api/tweets/"
  10. const responseType = "json"
  11. xhr.responseType = responseType
  12. xhr.open(method, url)
  13. xhr.onload = function() {
  14. callback(xhr.response, xhr.status)
  15. }
  16. xhr.onerror = function (e) {
  17. console.log(e)
  18. callback({"message": "The request was an error"}, 400)
  19. }
  20. xhr.send()
  21. }
  • then import and export in /twittme/src/lookup/index.js ```javascript import {loadTweets} from ‘./lookup’

export { loadTweets }

  1. - import loadTweets() again in /twittme/src/tweets/components.js
  2. ```javascript
  3. //...
  4. import {loadTweets} from '../lookup'
  5. //...

refresh to check
image.png
another import and export is also successful, and now we know how to apply js modules.

Improved Action Btn:

  • add new actions in /twittme/src/tweets/components.js and handle them ```javascript export function ActionBtn(props) { const {tweet, action} = props const className = props.className ? props.className : ‘btn btn-primary btn-sm’

    const actionDisplay = action.display ? action.display : ‘Action’ //ensure that we have an action const display = action.type === ‘like’ ? ${tweet.likes} ${actionDisplay} : actionDisplay //new

    //return action.type === ‘like’ ? : null return }

export function Tweet(props) { //… return

{tweet.id} - {tweet.content}

}

  1. refresh and we have new buttons for each action<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1593436583710-1badbc1d-8089-44b1-b8be-2262f48140a3.png#align=left&display=inline&height=473&margin=%5Bobject%20Object%5D&name=image.png&originHeight=945&originWidth=1811&size=119035&status=done&style=none&width=905.5)
  2. - then add a callback method in /twittme/src/tweets/components.js
  3. ```javascript
  4. export function ActionBtn(props) {
  5. const {tweet, action} = props
  6. const className = props.className ? props.className : 'btn btn-primary btn-sm'
  7. const actionDisplay = action.display ? action.display : 'Action' //ensure that we have an action
  8. let likes = tweet.likes//new
  9. const handleClick = (event) => { //new
  10. event.preventDefault()
  11. if (action.type === 'like') {
  12. console.log(tweet.likes+1)
  13. likes = tweet.likes + 1
  14. }
  15. }
  16. //const display = action.type === 'like' ? `${tweet.likes} ${actionDisplay}` : actionDisplay
  17. const display = action.type === 'like' ? `${likes} ${actionDisplay}` : actionDisplay
  18. //return <button className={className}>{display}</button>
  19. return <button className={className} onClick={handleClick}>{display}</button>
  20. }

refresh and click a like button:
image.png
even though we have a “1” in the console log, but that’s because it’s tweet.likes+1
The button still shows 0 Likes so we haven’t change actual states.

Understanding setState Hook:

  • change components.js to make sure tweet.likes+1 become actual states

    1. export function ActionBtn(props) {
    2. const {tweet, action} = props
    3. const [likes, setLikes] = useState(tweet.likes ? tweet.likes : 0) //new
    4. const className = props.className ? props.className : 'btn btn-primary btn-sm'
    5. const actionDisplay = action.display ? action.display : 'Action' //ensure that we have an action
    6. //let likes = tweet.likes
    7. const handleClick = (event) => {
    8. event.preventDefault()
    9. if (action.type === 'like') {
    10. setLikes(tweet.likes+1)
    11. //console.log(tweet.likes+1)
    12. //likes = tweet.likes + 1
    13. }
    14. }
    15. const display = action.type === 'like' ? `${likes} ${actionDisplay}` : actionDisplay
    16. return <button className={className} onClick={handleClick}>{display}</button>
    17. }

    refresh and ‘like’
    image.png
    this time “0 Likes” to “1 Likes”, but still not actually saved.

  • a user only can like a tweet for once, so modify in /twittme/src/tweets/components.js

    1. export function ActionBtn(props) {
    2. const {tweet, action} = props
    3. const [likes, setLikes] = useState(tweet.likes ? tweet.likes : 0)
    4. //const [justClicked, setJustClicked] = useState(false)
    5. const [userLike, setUserLike] = useState(tweet.userLike === true ? true : false) //success the like state of user
    6. const className = props.className ? props.className : 'btn btn-primary btn-sm'
    7. const actionDisplay = action.display ? action.display : 'Action' //ensure that we have an action
    8. const handleClick = (event) => {
    9. event.preventDefault()
    10. if (action.type === 'like') {
    11. //if(justClicked === true){
    12. if(userLike === true){
    13. setLikes(likes-1)
    14. setUserLike(false)
    15. } else {
    16. setLikes(likes+1)
    17. setUserLike(true)
    18. }
    19. //setLikes(tweet.likes+1)
    20. }
    21. }
    22. const display = action.type === 'like' ? `${likes} ${actionDisplay}` : actionDisplay
    23. return <button className={className} onClick={handleClick}>{display}</button>
    24. }

    refresh and click likes, the number of likes will -1 if the current user just clicked like for this one.

Handling a Form in React:

In this part we submit new forms in reactjs.

  • add TweetsComponent() in /twittme/src/tweets/components.js

    1. export function TweetsComponent(props) {
    2. const handleSubmit = (event) => {
    3. event.preventDefault()
    4. console.log(event)
    5. }
    6. return <div className={props.className}>
    7. <div className='col-12 mb-3'>
    8. <form onSubmit={handleSubmit}>
    9. <textarea className='form-control' name='tweet'>
    10. </textarea>
    11. <button type='submit' className='btn btn-primary my-3'>Tweet</button>
    12. </form>
    13. </div>
    14. <TweetsList />
    15. </div>
    16. }
  • import and export it in /twittme/src/tweets/index.js ```javascript //import {ActionBtn, Tweet, TweetsList} from ‘./components’ import {ActionBtn, Tweet, TweetsList, TweetsComponent} from ‘./components’

export { ActionBtn, Tweet, TweetsList, TweetsComponent //new }

  1. - apply TweetsComponent in App.js
  2. ```javascript
  3. //import {TweetsList} from './tweets'
  4. import {TweetsComponent} from './tweets'
  5. function App() {
  6. return (
  7. <div className="App">
  8. <header className="App-header">
  9. <img src={logo} className="App-logo" alt="logo" />
  10. <p>
  11. Edit <code>src/App.js</code> and save to reload.
  12. </p>
  13. <div>
  14. <TweetsComponent /> <!--new-->
  15. </div>
  16. <a
  17. className="App-link"
  18. href="https://reactjs.org"
  19. target="_blank"
  20. rel="noopener noreferrer"
  21. >
  22. Learn React
  23. </a>
  24. </header>
  25. </div>
  26. );
  27. }

refresh and test by sending something
image.png
and this tweet is not actually submitted, so we have to grab the value in the textarea:

  • add a reference in /twittme/src/tweets/compontents.js in refer the textarea to it.

    1. export function TweetsComponent(props) {
    2. const textAreaRef = React.createRef() //new
    3. const handleSubmit = (event) => {
    4. event.preventDefault()
    5. console.log(event)
    6. console.log(textAreaRef.current.value) //new
    7. }
    8. return <div className={props.className}>
    9. <div className='col-12 mb-3'>
    10. <form onSubmit={handleSubmit}>
    11. <textarea ref={textAreaRef} required={true} className='form-control' name='tweet'>
    12. </textarea>
    13. <button type='submit' className='btn btn-primary my-3'>Tweet</button>
    14. </form>
    15. </div>
    16. <TweetsList />
    17. </div>
    18. }

    send something:
    image.png
    so textAreaRef.current.value is the value of the input.

  • add more details:

    1. export function TweetsComponent(props) {
    2. const textAreaRef = React.createRef() //new
    3. const handleSubmit = (event) => {
    4. event.preventDefault()
    5. //console.log(event)
    6. //console.log(textAreaRef.current.value)
    7. const newVal = textAreaRef.current.value
    8. console.log(newVal)
    9. textAreaRef.current = '' //after the submission, clear the textarea
    10. }
    11. return <div className={props.className}>
    12. <div className='col-12 mb-3'>
    13. <form onSubmit={handleSubmit}>
    14. <textarea ref={textAreaRef} required={true} className='form-control' name='tweet'>
    15. </textarea>
    16. <button type='submit' className='btn btn-primary my-3'>Tweet</button>
    17. </form>
    18. </div>
    19. <TweetsList />
    20. </div>
    21. }

    and after the tweet is sent, the textarea will be cleared.
    However, the tweet still cannot be actually submitted, so that we have to do more works.