(2024/04/06更新) 因應React在18後更新了許多不同的語法,更新後的教學之後將陸續放在 新的blog 中,歡迎讀者到該處閱讀,我依然會回覆這邊的提問
過去我們使用圖片是直接在src屬性寫路徑:
<img src="./rice.jpg" alt="Rice"/>
在React直接這樣做是不行的,如果要使用jpg、png這種圖片,方式有兩種:
import Rice from "./rice.jpg";
<img src={Rice} alt="Rice"/>
<img src={require("./rice.jpg")} alt="Rice"/>
雖然我們之前的範例都是使用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),所以會直接附上這些文章的連結提供給大家參考。
因為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通常沒辦法馬上做完,又因為js的非同步特性,在執行需要新state的動作時很容易接收到舊的state值。
解決方法是遇到需要新state的動作時,把相關操作放在setState的第二個參數函式,setState會等更新完state後再執行。 我們在講setState那篇有提到 【React.js入門 - 12】 state 與 詳解setState語法。
因為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資料、函式時,剛開始很容易照以前的習慣直接用屬性="綁上去的東西",這樣的話等同於在傳字串。
解決方法是使用Props-Type插件。 這次鐵人賽 神Q超人 大大 也有特別介紹 Day10 | Props 太多, Component 就容易出錯, 就讓 Prop-Types 替你把關吧!
在使用插件時,有可能因為插件本身的運作並沒有配合react的生命週期而產生一些問題,要視情況調整使用方法,或是改用專為react設計的插件。(例如:Bootstrap會建議改用React Bootstrap)
專案結構變大之後,很容易在同一個js檔中JSX散佈各處,加上Component越來越多,整體結構會變成超長超複雜的巢狀結構。在一開始設計架構的時候要多留意。解決辦法之一是多利用React hook,讓程式碼盡量不要那麼笨重。
此外,React在Ver.16.2.0提供了一個新工具-Fragment。
在【React.js入門 - 05】 JSX (上)時,我們曾說:
- 只能傳遞一個元素
這裡的意思是「傳遞時是傳一個元素」,講白話就是如果你有一堆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都要綁一次函式,一次很多input時就會要綁很多次。
解決辦法是使用「非控制組件」,這是相對於之前提過的控制組件。可以查一下相關資訊。
在【React.js入門 - 21】 各階層Component的溝通中,我們提了一些土法煉鋼的傳遞方法,但是今天當很多地方都有一堆中繼站元件都要綁props,這樣土法煉鋼的方式易讀性不但差,使用上也很容易出錯。
比較好的方法是使用Redux 和 React 內建的Context api,它們主要都是用來管理state和action,最近一直在被互相比較和討論。鐵人賽滿多人有寫到的,youtube上也有人做影片來講這件事。
雖然React已經發展了一段時間,但這幾年仍然不斷的更新一些細節,從react hook、react-router-dom到生命週期的更動,必須要不斷關注,否則很容易一段時間後發現原來的寫法已經不能使用。