iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 29
0
Modern Web

給初入JS框架新手的React.js入門系列 第 29

【React.js入門 - 29】 使用圖片、使用css檔、新手容易遇到的問題

使用圖片

過去我們使用圖片是直接在src屬性寫路徑:

<img src="./rice.jpg" alt="Rice"/>

在React直接這樣做是不行的,如果要使用jpg、png這種圖片,方式有兩種:

  1. import後再使用
    import Rice from "./rice.jpg";
    
    <img src={Rice} alt="Rice"/>
    
  2. 直接在src使用require
    <img src={require("./rice.jpg")} alt="Rice"/>
    

使用css檔

雖然我們之前的範例都是使用inline-style,也就是在js中寫style,但是react是可以使用css檔的。例如我們在【React.js入門 - 28】 我要更多更多的分頁 - react-router-dom (下) 中完成的Layout就能以這樣分檔。
原本的Layout.js:

import React from 'react';
import {Link} from 'react-router-dom';

const Layout=(props)=>{
    const StyleSheet={
        width:"100vw",
        height:"100vh",
        backgroundColor:(props.location.pathname==="/")?"#FF2E63":"#08D9D6",
        display: "flex",
        alignItems:"center",
        justifyContent:"center",
        flexDirection:"column"
    }

    return(
       
        <div style={StyleSheet}>
            <nav>
                <Link to="/">點我連到第一頁</Link>
                <Link to="/second" style={{marginLeft:"20px"}}>點我連到第二頁</Link>
            </nav>
            {props.children}
        </div>
    );
}
export default Layout;

分檔後:
Layout.css

.Layout{
    width:100vw;
    height:100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction:column;
}

接著在需要的js檔中根據css檔位置引入就可以了。

Layout.js

import React from 'react';
import {Link} from 'react-router-dom';
import "./Layout.css";

const Layout=(props)=>{
    const StyleSheet={
        backgroundColor:(props.location.pathname==="/")?"#FF2E63":"#08D9D6",
    }
    return(
       
        <div className="Layout" style={StyleSheet}>
            <nav>
                <Link to="/">點我連到第一頁</Link>
                <Link to="/second" style={{marginLeft:"20px"}}>點我連到第二頁</Link>
            </nav>
            {props.children}
        </div>
    );
}
export default Layout;

注意inline-style的優先權會大於css檔,所以當同個style屬性在兩邊都被設定時,會顯示的是inline-style。

其他新手容易遇到的問題

如果是一路從第一篇跟到這邊的人,相信你已經可以用React寫出大部份你想實現的功能了。所以我想用這篇整理一下自己覺得新手開發React專案容易遇到的狀況,以及我所知道的解決辦法。我很菜,所以如果有更好的辦法也歡迎在留言告訴我QQ。其中有些解決方案在這次鐵人賽中有的人會獨立出來一篇,很詳細又完整,我自己在這邊講也不見得能講清楚(而且我也不一定有用過XD),所以會直接附上這些文章的連結提供給大家參考。

引入在不同component的css檔最後會互相影響

因為create-react-app預設是最後打包成一個app.js和app.css,即使原本不同的css檔引入在不同component,但使用同 className/id 的css指令還是會互相影響

最土法煉鋼的解決方法是讓Component最外層用來當container的div都有不同的className,並在引入該Component的css檔中加入限定在此class的條件。或是用inline-style(在js中寫style)去設定想要不同的style屬性。

比較聰明的方法是用像是styled-component這種插件來解決,詳情可以參考這次鐵人賽當中 老莫Kyle 大大的【Day 12】Styled-component

setState需要時間

setState通常沒辦法馬上做完,又因為js的非同步特性,在執行需要新state的動作時很容易接收到舊的state值。

解決方法是遇到需要新state的動作時,把相關操作放在setState的第二個參數函式,setState會等更新完state後再執行。 我們在講setState那篇有提到 【React.js入門 - 12】 state 與 詳解setState語法

state和props容易和其他變數/函數搞混

因為state和props中的data和function是獨立的scope,其他scope也可以用和已經存在於state和props相同的名稱命名變數/函數。然而vscode的自動補完程式碼功能在寫出this關鍵字時就會跳出state/props中的data和function,撰寫時很容易不小心把this.state.名稱寫成this.名稱

例如,這樣的寫法中,三個percent都是不同且不會互相影響的:

class ProgressDIY extends Component{
  constructor(props) {
    super(props);
    this.state={ 
        percent:0,
    }
  }
  
  componentDidMount(){
      let percent="我是誰?";
  }
  
  render(){
      let percent = true;
  }
}

剛開始容易忘記JSX綁定js要加{}

前面有提過,這裡再提一次。

在JSX綁定js資料、函式時,剛開始很容易照以前的習慣直接用屬性="綁上去的東西",這樣的話等同於在傳字串。

解決方法是使用Props-Type插件。 這次鐵人賽 神Q超人 大大 也有特別介紹 Day10 | Props 太多, Component 就容易出錯, 就讓 Prop-Types 替你把關吧!

React生命週期所引發的問題

在使用插件時,有可能因為插件本身的運作並沒有配合react的生命週期而產生一些問題,要視情況調整使用方法,或是改用專為react設計的插件。(例如:Bootstrap會建議改用React Bootstrap)

可讀性被JSX和巢狀結構降低

專案結構變大之後,很容易在同一個js檔中JSX散佈各處,加上Component越來越多,整體結構會變成超長超複雜的巢狀結構。在一開始設計架構的時候要多留意。解決辦法之一是多利用React hook,讓程式碼盡量不要那麼笨重。

此外,React在Ver.16.2.0提供了一個新工具-Fragment

【React.js入門 - 05】 JSX (上)時,我們曾說:

  1. 只能傳遞一個元素
    這裡的意思是「傳遞時是傳一個元素」,講白話就是如果你有一堆div、button......要傳的話,要用一個container把他們包起來。

然而很多時候,最外層的<div>除了包元素,是沒有任何作用的。而Fragment就是一個能讓我們一次傳遞多個元素、又不用在最外層包實體DOM元素的工具。使用方法是這樣的:

const testFunction =()=> {
    return( 
        <React.Fragment>
            <button> 大家好 </button>
            <h1> 我不好 </h1>
        </React.Fragment>
    );
}
//這個函式雖然傳了很多元素,但因為包在Fragment內,會被compiler視為正確語法

你也可以在import時單一引入Fragment,這樣就不用多寫「React.」

import React,{Fragment} from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';

const testFunction =()=> {
    return( 
        <Fragment>
            <button> 大家好 </button>
            <h1> 我不好 </h1>
        </Fragment>
    );
}

但是這樣還是要打「Fragment」,很多字、很麻煩,所以更簡潔的方式又出現了:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';

const testFunction =()=> {
    return( 
        <>
            <button> 大家好 </button>
            <h1> 我不好 </h1>
        </>
    );
}

這個<></>就是Fragment。

所以當初我們在講JSX只能傳遞一個元素,正確的說法應該是只能傳遞一個元素或是一個Fragment

多個input時,會太多純用來set的函式

因為每一個用來控制的input都要綁一次函式,一次很多input時就會要綁很多次。

解決辦法是使用「非控制組件」,這是相對於之前提過的控制組件。可以查一下相關資訊。

元件溝通時,太多中間隔很多層的祖父與孫子間溝通

【React.js入門 - 21】 各階層Component的溝通中,我們提了一些土法煉鋼的傳遞方法,但是今天當很多地方都有一堆中繼站元件都要綁props,這樣土法煉鋼的方式易讀性不但差,使用上也很容易出錯。

比較好的方法是使用Redux 和 React 內建的Context api,它們主要都是用來管理state和action,最近一直在被互相比較和討論。鐵人賽滿多人有寫到的,youtube上也有人做影片來講這件事。

小結 - React仍然是一個新的技術

雖然React已經發展了一段時間,但這幾年仍然不斷的更新一些細節,從react hook、react-router-dom到生命週期的更動,必須要不斷關注,否則很容易一段時間後發現原來的寫法已經不能使用。


上一篇
【React.js入門 - 28】 我要更多更多的分頁 - react-router-dom (下)
下一篇
【React.js入門 - 30】 學了React之後,然後呢?
系列文
給初入JS框架新手的React.js入門31

尚未有邦友留言

立即登入留言