グローバルなstate管理
グローバルなstate管理
グローバルなstate管理あり・なしで管理者の時は編集リンクを表示させ、
一般ユーザの時は編集リンクを表示させないことを実装する
ツラい実装
まずはグローバルなstate管理なしで管理者の時は編集リンクを表示させ、
一般ユーザの時は編集リンクを表示させないことを実装する
管理者ユーザと一般ユーザ用のボタンを用意する
import { useHistory } from "react-router-dom"; import styled from "styled-components"; import { SecondaryButton } from "../atoms/button/SecondaryButton";export const Top = () => { const history = useHistory();
const onClickAdmin = () => history.push({ pathname: "/users", state: { isAdmin: true } }); const onClickGeneral = () => history.push({ pathname: "/users", state: { isAdmin: false } });
return ( <SContainer> <h2>TOPページです</h2> <SecondaryButton onClick={onClickAdmin}>管理者ユーザ</SecondaryButton> <br /> <br /> <SecondaryButton onClick={onClickGeneral}>一般ユーザ</SecondaryButton> </SContainer> ); };
const SContainer = styled.div
text-align: center;
;
SecondaryButtonはonClickを受け取れるようになっている
import styled from "styled-components"; import { BaseButton } from "./BaseButton";export const SecondaryButton = (props) => { const { children, onClick } = props;
return <SButton onClick={onClick}>{children}</SButton>; };
const SButton = styled(BaseButton)
background-color: #11999e;
;
ページ遷移時のstateをuseLocationで取得
import { useLocation } from "react-router-dom"; import styled from "styled-components"; import { SearchInput } from "../molecules/SearchInput"; import { UserCard } from "../organisms/user/UserCard";const users = [...Array(10).keys()].map((val) => { return { id: val, name:
Bob$<span class="synIdentifier">{</span>val<span class="synIdentifier">}</span>
, image: "https://source.unsplash.com/2l0CWTpcChI", email: "12345@example.com", phone: "090-1111-2222", company: { name: "テスト株式会社" }, website: "https://google.com" }; });export const Users = () => { const { state } = useLocation(); const isAdmin = state ? state.isAdmin : false;
return ( <SContainer> <h2>ユーザー一覧</h2> <SearchInput /> <SUserArea> {users.map((obj) => ( <UserCard key={obj.id} user={obj} isAdmin={isAdmin} /> ))} </SUserArea> </SContainer> ); };
const SContainer = styled.div
text-align: center; flex-direction: column; align-items: center; padding: 24px;
;const SUserArea = styled.div
padding-<span class="synStatement">top</span>: 40px; width: 100%; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 20px;
;
渡されたisAdminを受け取る
import styled from "styled-components"; import { Card } from "../../atoms/card/Card"; import { UserIconWithName } from "../../molecules/user/UserIconWithName";export const UserCard = (props) => { const { user, isAdmin } = props;
return ( <Card> <UserIconWithName image={user.image} name={user.name} isAdmin={isAdmin} /> <SDL> <dt>メール</dt> <dd>{user.email}</dd> <dt>TEL</dt> <dd>{user.phone}</dd> <dt>会社名</dt> <dd>{user.company.name}</dd> <dt>WEB</dt> <dd>{user.website}</dd> </SDL> </Card> ); };
const SDL = styled.dl
text-align: left; margin-bottom: 0px; dt <span class="synIdentifier">{</span> <span class="synStatement">float</span>: left; <span class="synIdentifier">}</span> dd <span class="synIdentifier">{</span> padding-left: 32px; padding-bottom: 8px; overflow-wrap: <span class="synStatement">break</span>-word; <span class="synIdentifier">}</span>
;
渡されたisAdminを受け取る
import styled from "styled-components";export const UserIconWithName = (props) => { const { image, name, isAdmin } = props; return ( <SContainer> <SImg height={160} width={160} src={image} alt={name} /> <SName>{name}</SName> {isAdmin && <SEdit>編集</SEdit>} </SContainer> ); };
const SContainer = styled.div
text-align: center;
;const SImg = styled.img
border-radius: 50%;
; const SName = styled.pfont-size: 18px; font-weight: bold; margin: 0; color: #40514e;
; const SEdit = styled.spantext-decoration: underline; color: #aaa; cursor: pointer;
;
これがもっと規模が大きくなるとツラい。
これをグローバルなstate管理で解決していく。
Context
$ mkdir -p src/providers
$ touch src/providers/UserProvider.jsx
コンテキストのコンポーネントを作成
import { createContext, useState } from "react";// 新しいコンテキストの作成 export const UserContext = createContext({});
export const UserProvider = (props) => { const { children } = props;
const [userInfo, setUserInfo] = useState(null);
return ( // valueというグローバルなstateを渡す <UserContext.Provider value={{ userInfo, setUserInfo }}> {children} </UserContext.Provider> ); };
Providerは要素を囲む必要がある
import { UserProvider } from "./providers/UserProvider"; import { Router } from "./router/Router"; import "./styles.css";export default function App() { return ( <UserProvider> <Router /> </UserProvider> ); }
コンテキストの値を参照していくにはuseContextを使用する
import { useContext } from "react"; import styled from "styled-components"; import { UserContext } from "../../../providers/UserProvider";export const UserIconWithName = (props) => { const { image, name } = props; const { userInfo } = useContext(UserContext); const isAdmin = userInfo ? userInfo.isAdmin : false;
return ( <SContainer> <SImg height={160} width={160} src={image} alt="プロフィール写真" /> <SName>{name}</SName> {isAdmin && <SEdit>編集</SEdit>} </SContainer> ); };
const SContainer = styled.div
text-align: center;
;const SImg = styled.img
border-radius: 50%;
; const SName = styled.pfont-size: 18px; font-weight: bold; margin: 0; color: #40514e;
; const SEdit = styled.spantext-decoration: underline; color: #aaa; cursor: pointer;
;
import { useContext } from "react"; import { useHistory } from "react-router-dom"; import styled from "styled-components"; import { UserContext } from "../../providers/UserProvider"; import { SecondaryButton } from "../atoms/button/SecondaryButton";export const Top = () => { const history = useHistory();
const { setUserInfo } = useContext(UserContext);
const onClickAdmin = () => { setUserInfo({ isAdmin: true }); history.push("/users"); }; const onClickGeneral = () => { setUserInfo({ isAdmin: false }); history.push("/users"); };
return ( <SContainer> <h2>TOPページです</h2> <SecondaryButton onClick={onClickAdmin}>管理者ユーザ</SecondaryButton> <br /> <br /> <SecondaryButton onClick={onClickGeneral}>一般ユーザ</SecondaryButton> </SContainer> ); };
const SContainer = styled.div
text-align: center;
;