React訓練營:Advanced React Hooks:useContext與useReducer
React Hooks
React Hooks 用於元件之間的資料傳遞,相當於用一個鉤子把函式呼叫過程中的變數“鉤”出來。
在React開發中,元件之間的資料傳遞需要學習的語法為Context和Reducer的方式進行資料傳遞
useContext
useContxt顧名思義,為上下文,在電腦科學中,有Context Free Language 和 Non-Context Free Language。在React中其實就是一種上下文的資料傳遞形式,例如元件A->元件B的單向資料流動,那麼根據常見的思路:也可以理解為生產者->消費者模式。
採用Context傳遞資料的案例:假設在一個社交系統中,App元件中記錄了登入的使用者資訊,此時如果要將這個資訊傳遞到使用者訊息的子元件中,並顯示出來:
- 在App元件中定義全域性的Context
export const UserContext = React.createContext();
App函式中定義狀態:
const [user,setUser] = React.useState('mttt');
const [posts,setPosts] = React.useState([]);
使用UserContext,render的返回值為:
<UserContext.Provider value={user}> <PostList posts={posts}></PostList> </UserContext.Provider>
- 在PostList元件中
import React from "react"; import Post from "./Post"; function PostList({posts}) { return <div> {posts.map((post,i)=>(<Post key={i} image={post.image} content={post.content} user={post.user}/>))} </div>; } export default PostList;
3.在Post 元件中
import React from "react";
import {UserContext} from '../App';
function Post({image,content,user}){
const currentUser = React.useContext(UserContext);
const isCurrentUser = currentUser === user;
return (
<>
<div>
<p>{content}</p>
<div style={{color:isCurrentUser&&'green'}}>{user}</div>
</div>
{image && (
<img
style={{height:100, width:200, obejct:'cover'}}
src = {URL.createObjectURL(image)}
alt = "Post cover"
/>)}
</>
);
}
export default Post;
資料流向為:
App元件到PostList元件,在函式的引數中使用變數{posts}
進行引數傳遞,PostList元件到Post元件,也是使用類似的方法,就像鉤子一樣,把資料勾出來,但是此時通過函式引數單方向傳遞資料,只能一層一層傳遞引數,演示的例子中,使用了Context也就是最上層的App元件中定義了<UserContext.Provider value={user}>
,作為資料生產者,而在最底層的Post元件中使用了const currentUser = React.useContext(UserContext);
作為資料的消費者,得到了頂層元件的資料,此時的資料可以跨層流動,相比於直接通過函式引數的資料傳遞,Context的方式傳遞,自由度更大!
使用Reducer
Reducer使用純函式的思想,引數為state和action,返回值為newState
state: state = {value: }
action:const action={type:"LOGIN_USER",payload:{ username:"xmmt"}};
const initialState = { user:""};
function userReducer(state=initialState,action){
switch(action.type){
case "LOGIN_USER": {
return {
...state,
user: action.payload.username };
}
case "LOGOUT_USER": {
...state,
user:""
}
default:
return state;
}
}
const loginAction = { type:"LOGIN_USER",payload:{username:"xmmt"}};
const logoutAction = { type:"LOGOUT_USER"}
userReducer(initialState,loginAction);
userReducer(initialState,logoutAction);
使用Reducer 對使用者評論列表進行新增和刪除
Reducer的定義,編寫state 和 action
function postReducer(state,action){
switch (action.type) {
case "ADD_POST":{
const newPost = action.payload.post;
return { posts:[newPost,...state.posts]};
}
case "DELETE_POST":{
const deletePostId = action.payload.id;
return { posts:state.posts.filter(post=>post.id!==deletePostId)}
}
default:
return state;
}
}
export default postReducer;
App元件
全域性變數posts儲存用於傳送的評論(微博)之類的內容:
export const PostContext = React.createContext({
posts:[]
});
function App(){
...
const [state,dispatch] = React.useReducer(postReducer,initialPostState);
return (
...
<PostList posts={state.posts}></PostList>
...
)
}
引數從App到PostList再到Post元件
import React from "react";
import {UserContext,PostContext} from '../App';
function Post({image,content,user,id}){
const { dispatch } = React.useContext(PostContext);
const currentUser = React.useContext(UserContext);
const isCurrentUser = currentUser === user;
function handleDeletePost(){
dispatch({type:"DELETE_POST",payload:{id:id}});
}
return (
<>
<div>
<p>{content}</p>
<div style={{color:isCurrentUser&&'green'}}>{user}</div>
</div>
{image && (
<img
style={{height:100, width:200, obejct:'cover'}}
src = {URL.createObjectURL(image)}
alt = "Post cover"
/>)}
{ isCurrentUser && <button onClick={handleDeletePost}>Delte</button>}
</>
);
}
export default Post;
資料流向