引数にsizeを渡すことで、一つのメソッドでtoggleの切り替えが可能に
<Dropdown isOpen={this.state.isDropdownOpenS} toggle={() => this.onToggleDropdown('S')}>
- setState内部で変数を使う時は、[]でくくる。
- テンプレートリテラルのように
${}
で括ろうとするとエラーが出る。
onToggleDropdown(size) { this.setState({ [`isDropdownOpen${size}`]: !this.state[`isDropdownOpen${size}`] }); }
<Dropdown isOpen={this.state.isDropdownOpenS} toggle={() => this.onToggleDropdown('S')}>
ReactStrapのUncontrolledDropdown
すごいことを教えてもらった。 公式react-strap: dropdownbutton external_link
- UncontrolledDropdown(要調査:まだ完全理解していない)
- isOpenやonToggleなどのpropsが不要
- caretでtoggleの切り替えをしている
caret
onlyはつまりcaret = true
という意味
- 元々のdropdownより圧倒的に簡潔に書ける!!
TypeError: dropdownGroupMapping.map is not a function
-
TypeError: dropdownGroupMapping.map is not a function というエラーが出る。
-
このmapはArray.prototype.map() を指しており、dropdownGroupMappingが配列ではなくオブジェクトだから怒られているっぽい
- spread syntax(スプレッド構文)を使えばArrayとしてデータを取ってこられる?
- iterable protocolを実装したオブジェクトの値を取得したい
- 用語
- イテレーター iteratable object
-
Objectをmapする方法
Object.entries()
を使用する- Object.entriesメソッドは、引数に与えたオブジェクトが所有する、文字列をキーとした列挙可能なプロパティの組 [key, value] からなる配列を返します。
TypeError: value.switchPageListLimitationS is not a function
CustomizeFunctionSetting.jsx?51e3:127 Uncaught TypeError: value.switchPageListLimitationS is not a function
- 解決方法 bindをすればいい(忘れていた。)
関数で変更されたテキストの値をstateに適用しようとしたら、bind(this) とする必要があります。 javascriptの bind 関数を使って this を束縛します。 constructor で bind(this) としておかないと return文の文脈での this が指定されません。 特に、React.jsではbind(this)を使う場面が多いです。
疑問点
renderの外にmappingとObject.entries() 書いて呼び出す形にしたら表示されないのに、 renderの中にそれらを書いたらちゃんと表示されるのなんでだ
途中経過
- dropdownGroupMapping Objectを作成
CustomizeFunctionSettings.jsxrender() { const { t, adminCustomizeContainer } = this.props; const dropdownGroupMapping = { S: { label: 'admin:customize_setting.function_options.list_num_s', pageLimitation: adminCustomizeContainer.state.pageLimitationS, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationS, desc: 'admin:customize_setting.function_options.list_num_desc_s', }, M: { label: 'admin:customize_setting.function_options.list_num_m', pageLimitation: adminCustomizeContainer.state.pageLimitationM, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationM, desc: 'admin:customize_setting.function_options.list_num_desc_m', }, L: { label: 'admin:customize_setting.function_options.list_num_l', pageLimitation: adminCustomizeContainer.state.pageLimitationL, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationL, desc: 'admin:customize_setting.function_options.list_num_desc_l', }, XL: { label: 'admin:customize_setting.function_options.list_num_xl', pageLimitation: adminCustomizeContainer.state.pageLimitationXL, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationXL, desc: 'admin:customize_setting.function_options.list_num_desc_xl', }, };
- Objectにまとめたドロップダウンををmapで4つ表示することに成功
CustomizeFunctionSettings.jsx{Object.entries(dropdownGroupMapping).map(([key, value]) => { return ( <div className="form-group row" key={key}> <div className="offset-md-3 col-md-6 text-left"> <div className="my-0 w-100"> <label>{t(value.label)}</label> </div> <UncontrolledDropdown> <DropdownToggle className="text-right col-6" caret> <span className="float-left">{value.pageLimitation}</span> </DropdownToggle> <DropdownMenu className="dropdown-menu" role="menu"> <DropdownItem key={10} role="presentation" onClick={() => { value.switchPageListLimitation(10) }}> <a role="menuitem">10</a> </DropdownItem> <DropdownItem key={30} role="presentation" onClick={() => { value.switchPageListLimitation(30) }}> <a role="menuitem">30</a> </DropdownItem> <DropdownItem key={50} role="presentation" onClick={() => { value.switchPageListLimitation(50) }}> <a role="menuitem">50</a> </DropdownItem> </DropdownMenu> </UncontrolledDropdown> <p className="form-text text-muted"> {t(value.desc)} </p> </div> </div> ); })}
次の課題は、switchPageListLimitation
の引数をどうするか
各サイズで引数の中に入る値が変わるのでそれを考慮しないといけない
- keyのvalueに
dropdownMenu
を追加
CustomizeFunctionSettings.jsxrender() { const { t, adminCustomizeContainer } = this.props; const dropdownGroupMapping = { S: { label: 'admin:customize_setting.function_options.list_num_s', pageLimitation: adminCustomizeContainer.state.pageLimitationS, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationS, desc: 'admin:customize_setting.function_options.list_num_desc_s', dropdownMenu: [10, 30, 50], }, M: { label: 'admin:customize_setting.function_options.list_num_m', pageLimitation: adminCustomizeContainer.state.pageLimitationM, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationM, desc: 'admin:customize_setting.function_options.list_num_desc_m', dropdownMenu: [10, 30, 50], }, L: { label: 'admin:customize_setting.function_options.list_num_l', pageLimitation: adminCustomizeContainer.state.pageLimitationL, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationL, desc: 'admin:customize_setting.function_options.list_num_desc_l', dropdownMenu: [10, 30, 50], }, XL: { label: 'admin:customize_setting.function_options.list_num_xl', pageLimitation: adminCustomizeContainer.state.pageLimitationXL, switchPageListLimitation: adminCustomizeContainer.switchPageListLimitationXL, desc: 'admin:customize_setting.function_options.list_num_desc_xl', dropdownMenu: [10, 30, 50], }, };
- mapのネストで解決
CustomizeFunctionSettings.jsx{Object.entries(dropdownGroupMapping).map(([key, value]) => { return ( <div className="form-group row" key={key}> <div className="offset-md-3 col-md-6 text-left"> <div className="my-0 w-100"> <label>{t(value.label)}</label> </div> <UncontrolledDropdown> <DropdownToggle className="text-right col-6" caret> <span className="float-left">{value.pageLimitation}</span> </DropdownToggle> <DropdownMenu className="dropdown-menu" role="menu"> {value.dropdownMenu.map((v) => { return ( <DropdownItem key={v} role="presentation" onClick={() => { value.switchPageListLimitation(v) }}> <a role="menuitem">{v}</a> </DropdownItem> ); })} </DropdownMenu> </UncontrolledDropdown> <p className="form-text text-muted"> {t(value.desc)} </p> </div> </div> ); })}
mapの注意点
- 繰り返されるものだけをmapしましょう。
- mapするものをreturn文の中に入れるのを忘れずに!
完成形
- Objectで渡すのではなく、propsで渡すようにした。
CustomizeFunctionSetings.jsx<PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_s')} desc={t('admin:customize_setting.function_options.list_num_desc_s')} toggleLabel={adminCustomizeContainer.state.pageLimitationS} dropdownItemSize={[10, 20, 50, 100]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationS} /> <PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_m')} desc={t('admin:customize_setting.function_options.list_num_desc_m')} toggleLabel={adminCustomizeContainer.state.pageLimitationM} dropdownItemSize={[5, 10, 20, 50, 100]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationM} /> <PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_l')} desc={t('admin:customize_setting.function_options.list_num_desc_l')} toggleLabel={adminCustomizeContainer.state.pageLimitationL} dropdownItemSize={[20, 50, 100, 200]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationL} /> <PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_xl')} desc={t('admin:customize_setting.function_options.list_num_desc_xl')} toggleLabel={adminCustomizeContainer.state.pageLimitationXL} dropdownItemSize={[5, 10, 20, 50, 100]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationXL} />
PagingSizeUncontrolledDropdown.jsximport React from 'react'; import PropTypes from 'prop-types'; import { withTranslation } from 'react-i18next'; import { UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem, } from 'reactstrap'; const PagingSizeUncontrolledDropdown = (props) => { function dropdownItemOnClickHandler(num) { if (props.onChangeDropdownItem === null) { return; } props.onChangeDropdownItem(num); } return ( <React.Fragment> <div className="form-group row"> <div className="offset-md-3 col-md-6 text-left"> <div className="my-0 w-100"> <label>{props.label}</label> </div> <UncontrolledDropdown> <DropdownToggle className="text-right col-6" caret> <span className="float-left">{props.toggleLabel}</span> </DropdownToggle> <DropdownMenu className="dropdown-menu" role="menu"> {props.dropdownItemSize.map((num) => { return ( <DropdownItem key={num} role="presentation" onClick={() => dropdownItemOnClickHandler(num)}> <a role="menuitem">{num}</a> </DropdownItem> ); })} </DropdownMenu> </UncontrolledDropdown> <p className="form-text text-muted"> {props.desc} </p> </div> </div> </React.Fragment> ); }; PagingSizeUncontrolledDropdown.propTypes = { t: PropTypes.func.isRequired, // i18next label: PropTypes.string, toggleLabel: PropTypes.number, dropdownItemSize: PropTypes.array, desc: PropTypes.string, onChangeDropdownItem: PropTypes.func, }; export default withTranslation()(PagingSizeUncontrolledDropdown);