ダメなパターン
export default function QuestionsSheet({_questions}) {
const [questions, setQuestions] = useState(_questions);
// 質問追加処理
const AddQuestion = () => {
setQuestions((prevQuestions) => {
// 直前のデータを追加データの基にする
var addData = Object.assign({},prevQuestions[prevQuestions.length - 1]);
// 基データを初期化
addData.question_num = Number(addData.question_num) + 1;
addData.question = '';
addData.element = [];
addData.options = [];
prevQuestions.push(addData); // 追加
return prevQuestions;
}); // state更新
}
// 質問削除処理
const DeleteQuestion = (index) => {
// state更新
setQuestions(prevQuestions => {
if(prevQuestions.length == 1){
prevQuestions[0].question_num =+ 1
prevQuestions[0].question = "";
prevQuestions[0].element = [];
prevQuestions[0].options = [];
}else{
prevQuestions.splice(index, 1);
}
return prevQuestions;
});
}
return (
<Grid container spacing={2}>
{questions.map((data, index) => {
return(
<Grid item xs={12} md={12} lg={12} >
<Paper style={styles.contentItem} >
<label onClick={() => DeleteQuestion(index)}>{data['question']}</label>
</Paper>
</Grid>
);
})}
<Grid item xs={5} md={3} lg={2} >
<Button
type="button"
variant="outlined"
style={styles.fillWidthButton}
onClick={() => AddQuestion()}
>
ページ追加
</Button>
</Grid>
</Grid>
);
}
説明
いつも通りsetStateを使って、クリック時に配列を操作したもので更新してます。ですがこれだとレンダリングが行われず、画面に配列の内容が反映されません。
問題の原因は setQuestions() に渡された 配列が変更前と同じであることです。こちらについてめっちゃわかりやすく説明してくださっているサイトがこちらです。
簡単に言うとsetState内部でObject.is()を使用して前後での変更有無を判定しているため、現在stateのarray型変数に破壊的なメソッド(push、splice)を使ってはだめってこと見たい。全然知らんかった
OKなパターン
export default function QuestionsSheet({_questions}) {
const [questions, setQuestions] = useState(_questions);
// 質問追加処理
const AddQuestion = () => {
setQuestions((prevQuestions) => {
let prevQuestionsTmp = prevQuestions.slice();
// 直前のデータを追加データの基にする
var addData = Object.assign({},prevQuestionsTmp[prevQuestionsTmp.length - 1]);
// 基データを初期化
addData.question_num = Number(addData.question_num) + 1;
addData.question = '';
addData.element = [];
addData.options = [];
prevQuestionsTmp.push(addData); // 追加
return prevQuestionsTmp;
}); // state更新
}
// 質問削除処理
const DeleteQuestion = (index) => {
// state更新
setQuestions(prevQuestions => {
let prevQuestionsTmp = prevQuestions.slice();
if(prevQuestionsTmp.length == 1){
prevQuestionsTmp[0].question_num =+ 1
prevQuestionsTmp[0].question = "";
prevQuestionsTmp[0].element = [];
prevQuestionsTmp[0].options = [];
}else{
prevQuestionsTmp.splice(index, 1);
}
return prevQuestionsTmp;
});
}
return (
<Grid container spacing={2}>
{questions.map((data, index) => {
return(
<Grid item xs={12} md={12} lg={12} >
<Paper style={styles.contentItem} >
<label onClick={() => DeleteQuestion(index)}>{data['question']}</label>
</Paper>
</Grid>
);
})}
<Grid item xs={5} md={3} lg={2} >
<Button
type="button"
variant="outlined"
style={styles.fillWidthButton}
onClick={() => AddQuestion()}
>
ページ追加
</Button>
</Grid>
</Grid>
);
}
説明
対策は現在の state を表す array を直接変更するのではなく、別の array 型変数を生成して更新してあげる。これだけ
最後に
えらく長い間困ってた(無理矢理jsで追加・削除してた)けど、分かるとあっさり。何よりリンク先のサイトがわかりやすかった。
これがわかれば色々Reactの謎が解けそう。