私が提案するアプローチは少し冗長ですが、複雑なアプリにかなりうまく拡張できることがわかりました。あなたはモーダルを表示したい場合は、記述したアクションを発射され、あなたが見たい、モーダル:
モーダルを表示するアクションをディスパッチする
this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})
(もちろん、文字列は定数にすることができます。簡単にするために、インライン文字列を使用しています。)
モーダル状態を管理するためのレデューサーの作成
次に、これらの値を受け入れるだけのリデューサーがあることを確認します。
const initialState = {
modalType: null,
modalProps: {}
}
function modal(state = initialState, action) {
switch (action.type) {
case 'SHOW_MODAL':
return {
modalType: action.modalType,
modalProps: action.modalProps
}
case 'HIDE_MODAL':
return initialState
default:
return state
}
}
/* .... */
const rootReducer = combineReducers({
modal,
/* other reducers */
})
すごい!これで、アクションをディスパッチすると、state.modal
が更新され、現在表示されているモーダルウィンドウに関する情報が含まれます。
ルートモーダルコンポーネントの記述
コンポーネント階層のルートで<ModalRoot>
、Reduxストアに接続されているコンポーネントを追加します。state.modal
適切なモーダルコンポーネントをリッスンして表示し、プロップをから転送しstate.modal.modalProps
ます。
// These are regular React components we will write soon
import DeletePostModal from './DeletePostModal'
import ConfirmLogoutModal from './ConfirmLogoutModal'
const MODAL_COMPONENTS = {
'DELETE_POST': DeletePostModal,
'CONFIRM_LOGOUT': ConfirmLogoutModal,
/* other modals */
}
const ModalRoot = ({ modalType, modalProps }) => {
if (!modalType) {
return <span /> // after React v15 you can return null here
}
const SpecificModal = MODAL_COMPONENTS[modalType]
return <SpecificModal {...modalProps} />
}
export default connect(
state => state.modal
)(ModalRoot)
ここで何をしましたか?ModalRoot
現在の読み出しmodalType
とmodalProps
からstate.modal
、それが接続されているが、そのように対応するコンポーネントレンダリングDeletePostModal
またはConfirmLogoutModal
。すべてのモーダルはコンポーネントです!
特定のモーダルコンポーネントの記述
ここには一般的な規則はありません。これらは、アクションをディスパッチしたり、ストア状態から何かを読み取ったり、たまたまモーダルになったりすることができる単なるReactコンポーネントです。
たとえば、次のDeletePostModal
ようになります。
import { deletePost, hideModal } from '../actions'
const DeletePostModal = ({ post, dispatch }) => (
<div>
<p>Delete post {post.name}?</p>
<button onClick={() => {
dispatch(deletePost(post.id)).then(() => {
dispatch(hideModal())
})
}}>
Yes
</button>
<button onClick={() => dispatch(hideModal())}>
Nope
</button>
</div>
)
export default connect(
(state, ownProps) => ({
post: state.postsById[ownProps.postId]
})
)(DeletePostModal)
DeletePostModal
それは任意の連結成分のような記事のタイトルと作品を表示できるようにストアに接続されている:それは含めて、アクションをディスパッチすることができhideModal
、それ自体を非表示にする必要があるとき。
プレゼンテーションコンポーネントの抽出
すべての「特定の」モーダルに同じレイアウトロジックをコピーして貼り付けるのは面倒です。しかし、コンポーネントがありますよね?そのため、特定のモーダルの機能はわからないが、その外観を処理するプレゼンテーション <Modal>
コンポーネントを抽出できます。
次に、などの特定のモーダルDeletePostModal
でレンダリングに使用できます。
import { deletePost, hideModal } from '../actions'
import Modal from './Modal'
const DeletePostModal = ({ post, dispatch }) => (
<Modal
dangerText={`Delete post ${post.name}?`}
onDangerClick={() =>
dispatch(deletePost(post.id)).then(() => {
dispatch(hideModal())
})
})
/>
)
export default connect(
(state, ownProps) => ({
post: state.postsById[ownProps.postId]
})
)(DeletePostModal)
<Modal>
アプリケーションで受け入れることができる一連の小道具を思いつくのはあなた次第ですが、私はあなたがいくつかの種類のモーダル(たとえば、情報モーダル、確認モーダルなど)とそれらのためのいくつかのスタイルを持っていると想像します。
アクセシビリティと外部クリックまたはEscキーの非表示
モーダルに関する最後の重要な部分は、通常、ユーザーが外側をクリックするかEscapeキーを押したときに非表示にすることです。
これを実装することについてアドバイスする代わりに、自分で実装しないことをお勧めします。アクセシビリティを考慮して正しく理解するのは難しい。
代わりに、などのアクセス可能な既製のモーダルコンポーネントを使用することをお勧めしますreact-modal
。それは完全にカスタマイズ可能で、好きなものをその中に置くことができますが、それはアクセシビリティを正しく処理するので、視覚障害者がまだあなたのモーダルを使用することができます。
アプリケーションに固有の小道具を受け入れ、子ボタンやその他のコンテンツを生成react-modal
する独自の<Modal>
ものでラップすることもできます。それはすべて単なるコンポーネントです!
その他のアプローチ
それを行う方法は複数あります。
一部の人々は、このアプローチの冗長性が気に入らず、「ポータル」と呼ばれる手法でコンポーネント内に<Modal>
直接レンダリングできるコンポーネントを好む。ポータルを使用すると、コンポーネントをDOM内の所定の場所に実際にレンダリングしながら、コンポーネントを内部にレンダリングできます。これは、モーダルに非常に便利です。
実際、react-modal
私が以前にリンクしたことはすでに内部でそれを行っているので、技術的には上からそれをレンダリングする必要さえありません。表示したいモーダルを表示しているコンポーネントから分離するのは良いことですが、コンポーネントからreact-modal
直接使用して、上で書いたもののほとんどをスキップすることもできます。
両方のアプローチを検討して実験し、アプリとチームに最適なものを選択することをお勧めします。