複数のタブに分かれるフォームを作成しています。React Context API を使用して、フォーム入力の状態を取得し、プロバイダーで状態を設定しています。これは、フォームの入力場所にある私のコンシューマーでうまくいきます。ただし、フォームの最後のタブには、すべてのステップでフォームの入力を確認できるビューもあります。その場所のプロバイダーは、React Dev Tools のデフォルトの状態値のみを表示するため、コンシューマーを更新できません。プロバイダーの 2 番目のインスタンス化の状態が更新されず、小道具としてレビュー セクションに渡されない理由がわかりません。をプロバイダーからエクスポートcreateContext()
し、レビュー セクション コンシューマーとフォーム入力セクション コンシューマーにインポートしています。
いくつかのコードを次に示します。
プロバイダー:
import React, { Component } from "react";
import moment from 'moment';
import PropTypes from 'prop-types';
const defaultDateState = moment();
const daysWeek = [
{name: 'All'},
{name: 'Sunday'},
{name: 'Monday'},
{name: 'Tuesday'},
{name: 'Wednesday'},
{name: 'Thursday'},
{name: 'Friday'},
{name: 'Saturday'}
];
const descMaxChars = 512;
export const BasicContext = React.createContext();
class BasicInfoProvider extends Component {
state = {
basicDesc: null,
startDate: defaultDateState,
endDate: defaultDateState,
daysOfWeek: null,
dayWeek: [],
dayWeekOptions: daysWeek,
descCharsLeft: descMaxChars,
};
//handle dates
handleChangeStart = (date) => {
console.log(date);
this.setState({
startDate: date
});
}
handleChangeEnd = (date) => {
this.setState({
endDate: date
});
}
handleChangeDesc = (e) => {
var input = e.target.value;
this.setState({
basicDesc: input,
descCharsLeft: descMaxChars - input.length
});
}
handleDaysWeek = (e) => {
console.log('days week selection', e.target.value)
const daySelection = e.target.value;
let daySelectionArr;
if(this.state.dayWeek.indexOf(daySelection) > -1) {
daySelectionArr = this.state.dayWeek.filter(s => s !== daySelection)
} else {
daySelectionArr = [...this.state.dayWeek, daySelection];
}
this.setState({ dayWeek: daySelectionArr });
}
render(){
return(
<React.Fragment>
<BasicContext.Provider value={{
state: this.state,
actions: {
handleChangeDesc: this.handleChangeDesc,
handleChangeEnd: this.handleChangeEnd,
handleChangeStart: this.handleChangeStart,
handleDaysWeek: this.handleDaysWeek
},
}}>
{this.props.children}
</BasicContext.Provider>
</React.Fragment>
)
}
}
export const contextTypes = {
basicDesc: PropTypes.string,
startDate: PropTypes.number,
endDate: PropTypes.number,
daysOfWeek: PropTypes.string,
dayWeek: PropTypes.array,
dayWeekOptions: PropTypes.array,
descCharsLeft: PropTypes.number,
}
export default BasicInfoProvider;
フォーム入力コンシューマー:
import React, { Component } from "react";
import DateElement from "../DateElement";
import { Button, ButtonToolbar, FormGroup, ControlLabel, Checkbox, FormControl, HelpBlock, Modal, Glyphicon } from "react-bootstrap";
import DatePicker from 'react-datepicker';
import Select from 'react-select';
import { BasicContext, contextTypes } from './BasicInfoProvider';
import {getContext} from '../../utilities/GetContext';
import 'react-datepicker/dist/react-datepicker.css';
import 'react-select/dist/react-select.css';
class BasicInfoForm extends Component {
render(){
return(
<section className="basic-info-section">
<BasicContext.Consumer >
{({state, actions}) => (
<React.Fragment>
<div className="row">
<FormGroup
controlId="desc-field"
>
<h3>Description</h3>
<FormControl
type="textarea"
value={state.basicDesc}
placeholder="Enter Your Description Here"
onChange={actions.handleChangeDesc}
maxLength="512"
/>
<FormControl.Feedback />
<HelpBlock>Characters left: {state.descCharsLeft}</HelpBlock>
</FormGroup>
</div>
<hr/>
<div className="row">
<div className="date-picker-duo col-sm-6">
<div className="col-xs-6">
<h3>Start Date</h3>
<DatePicker
inline
selected={state.startDate}
selectsStart
onChange={actions.handleChangeStart}
placeholderText='Click to select a date'
className="start-date-field"
/>
</div>
<div className="col-xs-6">
<h3>End Date</h3>
<DatePicker
inline
selected={state.endDate}
selectsend
onChange={actions.handleChangeEnd}
placeholderText='Click to select a date'
className="end-date-field"
/>
</div>
</div>
<FormGroup bsClass="col-sm-6" controlId="formControlsSelectMultiple">
<h3 className="days-checkbox-title">Days of the Week</h3>
{state.dayWeekOptions.map((day) => {
return(
<div className="weekday-checkbox col-xs-3">
<input
value={day.name}
onChange={actions.handleDaysWeek}
type="checkbox"
id={day.name}
key={day.name} />
<label htmlFor={day.name}>{day.name}</label>
</div>
)
})}
</FormGroup>
</div>
</React.Fragment>
)}
</BasicContext.Consumer>
</section>
);
}
};
export default BasicInfoForm = getContext(contextTypes)(BasicInfoForm);
レビュー セクションの利用者
import React from 'react';
import { BasicContext, contextTypes } from './BasicInfoProvider';
import {getContext} from '../../utilities/GetContext';
const finalReview = (props) => {
return(
<div className="row">
<h2>So you're all done with your new Promotion?</h2>
<p>Take another look at what you're about to submit and if you like how it looks, send it on through!</p>
<div className="well">
<header className="review-section-header">
<h3>Basic Info </h3>
<span className="review-edit" onClick={() => this.handleTabSelect(1)}>Edit Section</span>
</header>
<BasicContext.Consumer >
{({state}) => (
<div className="review-group">
<p>
<span className="review-title">Description: </span>
<span className="review-value">{state.basicDesc}</span>
</p>
<p>
<span className="review-title">Promotion Start Date: </span>
<span className="review-value">{state.startDate.toString()}</span>
</p>
<p>
<span className="review-title">Promotion End Date: </span>
<span className="review-value">{state.endDate.toString()}</span>
</p>
<p>
<span className="review-title">Promotion Days of the Week: </span>
<span className="review-value">
{state.dayWeek.map((day) => {
return( `${day}, `)
})}
</span>
</p>
</div>
)}
</BasicContext.Consumer>
</div>
</div>
)
}
const FinalReview = getContext(contextTypes)(finalReview);
export default FinalReview;
getContext
ContextTypes を渡す単純な HOC です。
BasicInfoForm
はフォーム内のタブに存在するフォームの子コンポーネントですが、プロバイダーはそこに直接行くので、タブは関係ないと思います。
私の期待は、が渡された関数を呼び出すBasicInfoProvider
ときにBasicInfoForm
更新され、その更新が使用されているすべての場所に反映されることですが、コンポーネントBasicInfoProvider
に使用されている場合はそうではありませんFinalReview