React 基礎:Props Drilling 和 useContext


Props Drilling

  • 直接來看範例
function DemoInnerBoxContent({ setTitle }) {
    return (
        <div>
            <button
                onClick={() => {
                    setTitle(Math.random())
                }}
                >Update title!</button>
        </div>    
    )
}

function DemoInnrerBox({ setTitle }) {
    return <DemoInnerBoxContext  setTitle={setTitle}  />
}

function DemoInner({ setTitle }) {
    return <DemoInnerContext  setTitle={setTitle}  />
}

export default function Demo() {
    const [title, setTitle] = useState("I am title!")
    return (
        <div>
            titile: {title}
            <DemoInner setTitle={setTitle} />
         </div>
    )
}

useContext

解決 Props Drilling 的問題。仍要達到父層想要傳遞資料、函式到子層的目的。

  1. 引入 { useContext, createContext }
  2. 在要提供資料的父層,加上 <TitleContext.Provider>
  3. 然後在裡面加上 {value={setTitle}},意思是把 setTilte 這個值傳下去
  4. 在目標子層,加入 const setTitle = useContext(TitleContext)
import { useContext, createContext } from "react"

const TitleContenxt = createContext()
const ColorContext = createContext()

function DemoInnerBoxContent() {
    const setTitle = useContext(TitleContext)
    const colors = useContext(ColorContext)
    return (
        <div>
            <button
                style={{
                    color: colors.primary
                }}
                onClick={() => {
                    setTitle(Math.random())
                }}
                >Update title!</button>
        </div>    
    )
}

function DemoInnrerBox() {
    return <DemoInnerBoxContext />
}

function DemoInner() {
    return <DemoInnerContext />
}

export default function Demo() {
    const [title, setTitle] = useState("I am title!")
    const [colors, setColors] = useState({
        primary: "#ff0000",
    });
    return (
        <ColorContext.Provider value={{colors
        }}>
            <TitleContext.Provider {value={setTitle}}>
                <div>
                    <button
                        onClick={() => {
                            setColors({
                                primary: "#00000F"
                            })
                        }}
                    >
                    titile: {title}
                    <DemoInner />
                 </div>
            </TitleContext.Provider>
        </ColoeContext.Provider>
    )
}

總結:以資料為中心去思考

資料改變會需要改變畫面的,它就是 state

  • 規劃 state
    以 TodoList 為例:把畫面上的資料拿下來,變成一個 data;接著 data 可以幫你還原成原本畫面。
{
    inputValue: 'abcde',
    todos: [
        {
            isChecked: true,
            content: 'ddw'
        }, {
            isChecked: false,
            content: 'todo1'
        }
    ],
    itemLeftCount: 1, // 非必要,derived state
    filter: active,
}
todoId: 2  // 需要改變但不影響畫面 useRef
  • State vs Derived State
    Derived State 是什麼?可以透過現有的 State 組合/計算而成

  • 舉個 Derived State 的例子

export default function App() {
    const [todos, setTodos] = useState ([
     {content: '1', id:1, isChecked: true },
     {content: '2', id:2 isChecked: false }
    ])
    const [filter, setFilter] = useState('active');
    const [value, setValue] = sueState('');

    // 使用 useCallback()
    const handleChange = React.useCallback((e) => {
        setValue(e.target.value)
    }, [todos])

    // 沒有改 Todos、filter ,每次 re-render 都還是要計算一次
    // 這時候 useMemo() 派上用場
    const filteredTodos = useMemo(() =>
        return todos.filter(todo => {
            if (filter === 'all') return true
            return filter === 'completed' ? todo.isChecked : !todo.isChecked
        }, [todos, filter])
         // 當 Todos、filter 改變的時候 filterTodos 會重算一次;反之,不會計算
     }
)

return (
    <div>
        <input value={value} onChange={e => setValue(handleChange)} />
        {filteredTodos.map((todo) => <div key={todo.id}>{todo.content}</div>))}
    </div>
)







Related Posts

由下到上:從 TCP/IP 開始談起

由下到上:從 TCP/IP 開始談起

[ 紀錄 ] 實戰練習 - 抽獎程式 (以 Express 實作後端 API )

[ 紀錄 ] 實戰練習 - 抽獎程式 (以 Express 實作後端 API )

簡明程式解題入門 - 陣列篇 I

簡明程式解題入門 - 陣列篇 I




Sponsored



Comments