MetaでInstagramを統括するアダム・モッセリ氏は「ThreadsはInstagram を利用しているため、現時点では統合された1つのアカウントのみだが、Threadsアカウントを個別に削除する方法を検討している」と、自身のThreadsで述べている。
Threadsアカウント削除にはInstagramごと削除する必要が 「Threadsだけ削除する方法、検討中」とMeta - ITmedia NEWS
⇧ Instagramのアカウントも一緒に削除されるのは、仕様として問題ありな気がしますかね...
Reactで『Warning: A component is changing an uncontrolled input to be controlled. 』というエラー
前回の、
⇧ の時に出ていたエラーで、正確には、
Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components at input at label at td at tr at tbody at table at form at div at Page1 (http://localhost:5173/src/page/shipment/create/page1.tsx?t=1688801019183:22:20) at RenderedRoute (http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=e3f7d3ae:3115:5) at Routes (http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=e3f7d3ae:3483:5) at ShipmentItemsProvider (http://localhost:5173/src/page/shipment/create/context.tsx:20:3) at Router (http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=e3f7d3ae:3430:15) at BrowserRouter (http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=e3f7d3ae:3851:5) at div at App (http://localhost:5173/src/App.tsx?t=1688801019183:26:35) at Provider (http://localhost:5173/node_modules/.vite/deps/react-redux.js?v=e3f7d3ae:1450:3) printWarning @ react-dom.development.js:86 error @ react-dom.development.js:60 updateWrapper @ react-dom.development.js:1813 updateProperties @ react-dom.development.js:10151 commitUpdate @ react-dom.development.js:11047 commitMutationEffectsOnFiber @ react-dom.development.js:24389 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24576 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24576 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24576 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24576 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24346 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24576 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24293 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24576 recursivelyTraverseMutationEffects @ react-dom.development.js:24273 commitMutationEffectsOnFiber @ react-dom.development.js:24432 commitMutationEffects @ react-dom.development.js:24243 commitRootImpl @ react-dom.development.js:26810 commitRoot @ react-dom.development.js:26682 finishConcurrentRender @ react-dom.development.js:25981 performConcurrentWorkOnRoot @ react-dom.development.js:25809 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533
って感じの警告なのだけど、
⇧ 上記サイト様が仰るには、Reactでcontrolled component化した<input>を扱う場合、value属性に初期値が設定されるようにしなきゃならんのだと。
Reactのルール、面倒くさいな...
【変更前】my-react-spa-app\src\page\shipment\create\page1.tsx
import React, { useContext, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { IInventoryShipmentPackingItemDto } from '../../../components/model/dto/shipment/inventory-shipment-packing-item-dto'; import { ShipmentItemsContext } from './context'; const Page1: React.FC = () => { const navigate = useNavigate(); const { shipmentItems, setShipmentItems }: any = useContext(ShipmentItemsContext); console.log("shipmentItems"); console.dir(shipmentItems); useEffect(() => { // 1ページ目に遷移した際に、shipmentItemsが未初期化の場合のみ初期化する if (shipmentItems.length === 0) { const initialShipmentItems: IInventoryShipmentPackingItemDto[] = Array.from( { length: 10 }, (_, index) => ({ itemNumber: index + 1, shipmentOrderNumber: '', shipmentPackingItemOrderNumber: '', numberOfOrders: 0, numberOfShipments: 0, inventoryShipmentPackingStoreItemDtoArr: [], }) ); console.log("initialShipmentItems"); console.dir(initialShipmentItems); setShipmentItems([...initialShipmentItems]); } }, [shipmentItems, setShipmentItems]); const handleShipmentItemChange = ( index: number , event: React.ChangeEvent<HTMLInputElement> ) => { event.preventDefault(); const { name, value } = event.target; console.log("handleShipmentItemChange"); console.dir(shipmentItems); setShipmentItems(shipmentItems.map((item: IInventoryShipmentPackingItemDto, itemIndex: number) => { let propertyName = name.split("_")[0]; if (index === itemIndex) { return { ...item ,shipmentOrderNumber: propertyName === 'shipmentOrderNumber'? value: item.shipmentOrderNumber ,shipmentPackingItemOrderNumber: propertyName === 'shipmentPackingItemOrderNumber'? value: item.shipmentPackingItemOrderNumber }; } else { return { ...item } } })) }; const handleNext = (index: number, e: React.MouseEvent<HTMLButtonElement>) => { e.preventDefault(); const clickedButtonParentRow = e.currentTarget.closest('tr'); const hiddenInput = clickedButtonParentRow?.querySelector( `input[name="itemNumber_${index}"]` ) as HTMLInputElement; const hiddenInputValue = hiddenInput?.value; const selectedRowIndex = Number(hiddenInputValue) -1; navigate('/page2', { state: { shipmentItems, selectedRowIndex } }); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // 1ページ目のフォームと2ページ目のフォームのデータをサーバーサイドに送信する処理 // ... // データ送信後に1ページ目に遷移する navigate('/page1'); }; return ( <div> <h2>ページ1</h2> <form onSubmit={handleSubmit}> <table> <thead className="content-header" style={{ tableLayout: 'fixed' }}> <tr> <th>No.</th> <th>出荷指示番号</th> <th>出荷指示梱包明細番号</th> <th></th> </tr> </thead> <tbody className="content-body"> {/* 1ページ目のフォームを10回ループ */} {Array.from({ length: 10 }, (_, index) => ( <tr key={index}> <td> <label> {index + 1} <span style={{ display: 'none' }}>Item Number:</span> <input type="hidden" id={`itemNumber_${index}`} name={`itemNumber_${index}`} value={shipmentItems[index]?.[`itemNumber`]} readOnly /> </label> </td> <td> <label> <span style={{ display: 'none' }}>Shipment Order Number:</span> <input type="text" id={`shipmentOrderNumber_${index}`} name={`shipmentOrderNumber_${index}`} value={shipmentItems[index]?.[`shipmentOrderNumber`] || ''} onChange={(e) => handleShipmentItemChange(index, e)} /> </label> </td> <td> <label> <span style={{ display: 'none' }}>Shipment Packing Item Order Number:</span> <input type="text" id={`shipmentPackingItemOrderNumber_${index}`} name={`shipmentPackingItemOrderNumber_${index}`} value={shipmentItems[index]?.[`shipmentPackingItemOrderNumber`] || ''} onChange={(e) => handleShipmentItemChange(index, e)} /> </label> </td> <td> <button type="button" onClick={(e) => handleNext(index, e)}> NEXT </button> </td> </tr> ))} </tbody> </table> <button type="submit">Submit</button> </form> </div> ); }; export default Page1;
【変更後】my-react-spa-app\src\page\shipment\create\page1.tsx
import React, { useContext, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { IInventoryShipmentPackingItemDto } from '../../../components/model/dto/shipment/inventory-shipment-packing-item-dto'; import { ShipmentItemsContext } from './context'; const Page1: React.FC = () => { const navigate = useNavigate(); const { shipmentItems, setShipmentItems }: any = useContext(ShipmentItemsContext); console.log("shipmentItems"); console.dir(shipmentItems); useEffect(() => { // 1ページ目に遷移した際に、shipmentItemsが未初期化の場合のみ初期化する if (shipmentItems.length === 0) { const initialShipmentItems: IInventoryShipmentPackingItemDto[] = Array.from( { length: 10 }, (_, index) => ({ itemNumber: index + 1, shipmentOrderNumber: '', shipmentPackingItemOrderNumber: '', numberOfOrders: 0, numberOfShipments: 0, inventoryShipmentPackingStoreItemDtoArr: [], }) ); console.log("initialShipmentItems"); console.dir(initialShipmentItems); setShipmentItems([...initialShipmentItems]); } }, [shipmentItems, setShipmentItems]); const handleShipmentItemChange = ( index: number , event: React.ChangeEvent<HTMLInputElement> ) => { event.preventDefault(); const { name, value } = event.target; console.log("handleShipmentItemChange"); console.dir(shipmentItems); setShipmentItems(shipmentItems.map((item: IInventoryShipmentPackingItemDto, itemIndex: number) => { let propertyName = name.split("_")[0]; if (index === itemIndex) { return { ...item ,shipmentOrderNumber: propertyName === 'shipmentOrderNumber'? value: item.shipmentOrderNumber ,shipmentPackingItemOrderNumber: propertyName === 'shipmentPackingItemOrderNumber'? value: item.shipmentPackingItemOrderNumber }; } else { return { ...item } } })) }; const handleNext = (index: number, e: React.MouseEvent<HTMLButtonElement>) => { e.preventDefault(); const clickedButtonParentRow = e.currentTarget.closest('tr'); const hiddenInput = clickedButtonParentRow?.querySelector( `input[name="itemNumber_${index}"]` ) as HTMLInputElement; const hiddenInputValue = hiddenInput?.value; const selectedRowIndex = Number(hiddenInputValue) -1; navigate('/page2', { state: { shipmentItems, selectedRowIndex } }); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // 1ページ目のフォームと2ページ目のフォームのデータをサーバーサイドに送信する処理 // ... // データ送信後に1ページ目に遷移する navigate('/page1'); }; return ( <div> <h2>ページ1</h2> <form onSubmit={handleSubmit}> <table> <thead className="content-header" style={{ tableLayout: 'fixed' }}> <tr> <th>No.</th> <th>出荷指示番号</th> <th>出荷指示梱包明細番号</th> <th></th> </tr> </thead> <tbody className="content-body"> {/* 1ページ目のフォームを10回ループ */} {Array.from({ length: 10 }, (_, index) => ( <tr key={index}> <td> <label> {index + 1} <span style={{ display: 'none' }}>Item Number:</span> <input type="hidden" id={`itemNumber_${index}`} name={`itemNumber_${index}`} value={shipmentItems[index]?.[`itemNumber`] || ''} readOnly /> </label> </td> <td> <label> <span style={{ display: 'none' }}>Shipment Order Number:</span> <input type="text" id={`shipmentOrderNumber_${index}`} name={`shipmentOrderNumber_${index}`} value={shipmentItems[index]?.[`shipmentOrderNumber`] || ''} onChange={(e) => handleShipmentItemChange(index, e)} /> </label> </td> <td> <label> <span style={{ display: 'none' }}>Shipment Packing Item Order Number:</span> <input type="text" id={`shipmentPackingItemOrderNumber_${index}`} name={`shipmentPackingItemOrderNumber_${index}`} value={shipmentItems[index]?.[`shipmentPackingItemOrderNumber`] || ''} onChange={(e) => handleShipmentItemChange(index, e)} /> </label> </td> <td> <button type="button" onClick={(e) => handleNext(index, e)}> NEXT </button> </td> </tr> ))} </tbody> </table> <button type="submit">Submit</button> </form> </div> ); }; export default Page1;
【変更前】my-react-spa-app\src\page\shipment\create\page2.tsx
import React, { useContext, useEffect, useState } from "react"; //import { render } from 'react-dom'; import { createRoot } from "react-dom/client"; import { useNavigate, useLocation } from "react-router-dom"; import { ShipmentItemsContext } from "./context"; import { IInventoryShipmentPackingItemDto } from "../../../components/model/dto/shipment/inventory-shipment-packing-item-dto"; import { IInventoryShipmentPackingStoreItemDto } from "../../../components/model/dto/shipment/inventory-shipment-packing-store-item-dto"; const Page2: React.FC = () => { const location = useLocation(); const navigate = useNavigate(); const shipmentItemsContext = useContext(ShipmentItemsContext); console.log("page2■ShipmentItemsContext"); console.dir(shipmentItemsContext); // const [isRender, setIsRender] = useState(true); // const [onloaded, setOnloaded] = useState(false); // const [isInit, setIsInit] = useState(false); const [isReadyRender, setIsReadyRender] = useState(false); if (!shipmentItemsContext) { // ShipmentItemsContextが未定義の場合のエラーハンドリング return null; } // 選択された行の番号 const selectedRowIndex = location.state?.selectedRowIndex || 0; // let stateShipmetItemArr: IInventoryShipmentPackingItemDto[] = location.state?.shipmentItems || []; console.log("location.state"); console.dir(location.state); //let [shipmentItems, setShipmentItems] = useState< // const [shipmentItems, setShipmentItems] = useState< // IInventoryShipmentPackingItemDto[] // >( // location.state && location.state.shipmentItems // ? location.state.shipmentItems // : [] // ); const { shipmentItems, setShipmentItems }: any = useContext(ShipmentItemsContext); console.log("shipmentItemsContext"); console.dir(shipmentItemsContext); // サーバーサイドからデータが取得された想定 let initShipmentItems: any = []; let tableBody: JSX.Element[] = [] as JSX.Element[]; let storeData = [ { packingChainStoreCode: "01", storeName: "保谷店" }, { packingChainStoreCode: "02", storeName: "大泉学園店" }, { packingChainStoreCode: "03", storeName: "練馬駅前店" }, { packingChainStoreCode: "04", storeName: "石神井公園店" }, { packingChainStoreCode: "05", storeName: "石神井台店" }, { packingChainStoreCode: "06", storeName: "練馬店" }, { packingChainStoreCode: "07", storeName: "中村橋店" }, { packingChainStoreCode: "08", storeName: "成増店" }, { packingChainStoreCode: "09", storeName: "田無店" }, { packingChainStoreCode: "10", storeName: "東中野店" }, { packingChainStoreCode: "11", storeName: "高円寺店" }, { packingChainStoreCode: "12", storeName: "笹塚店" }, { packingChainStoreCode: "13", storeName: "上池袋店" }, { packingChainStoreCode: "14", storeName: "東新宿店" }, { packingChainStoreCode: "15", storeName: "新宿店" }, { packingChainStoreCode: "16", storeName: "中野店" }, { packingChainStoreCode: "17", storeName: "三鷹店" }, { packingChainStoreCode: "18", storeName: "武蔵境店" }, { packingChainStoreCode: "19", storeName: "吉祥寺店" }, { packingChainStoreCode: "20", storeName: "西荻窪店" }, ]; React.useEffect(() => { const isAreadyInit: boolean = shipmentItems.every((item: IInventoryShipmentPackingItemDto) => item.inventoryShipmentPackingStoreItemDtoArr.length !== 0) if (isAreadyInit) { return; } console.log("【useEffect】【start】初回実行"); // 選択された行の情報を取得 // const selectedRow = location.state.shipmentItems[selectedRowIndex]; const copyShipmentItems = JSON.parse(JSON.stringify(shipmentItems)); console.log("copyShipmentItems"); console.dir(copyShipmentItems); const initialShipmentItems: IInventoryShipmentPackingItemDto[] = //location.state.shipmentItems.map((item: any, itemIndex: number) => { copyShipmentItems.map((item: any, itemIndex: number) => { // 店舗別の情報(更新用) let inventoryShipmentPackingStoreItemDtoArr: IInventoryShipmentPackingStoreItemDto[] = []; // 店舗別発注数合計 let totalStoreNumberOfOrders = 0; // 店舗別出荷数合計 let totalStoreNumberOfShipments = 0; // 店舗別の情報(作業用) const inventoryShipmentPackingStoreItemDtoWorkArr = item[selectedRowIndex]?.inventoryShipmentPackingStoreItemDtoArr || []; // サーバーからのデータを設定していく storeData.forEach((store: any, storeIndex: number) => { // 店舗別の情報 // const selected = inventoryShipmentPackingStoreItemDtoWorkArr.some( // (storeItem: any) => // storeItem?.packingChainStoreCode === store.packingChainStoreCode // ); // 選択されてる行の場合だけ実施 if (itemIndex === selectedRowIndex) { // 店舗別の発注数、出荷数の合計を算出する item.inventoryShipmentPackingStoreItemDtoArr.forEach( (itemStore: any, itemStoreIndex: number) => { // 発注数の合計 if (Number.isInteger(itemStore.numberOfOrders)) { totalStoreNumberOfOrders += itemStore.numberOfOrders; } // 出荷数の合計 if (Number.isInteger(itemStore.numberOfShipments)) { totalStoreNumberOfShipments += itemStore.numberOfShipments; } } ); } // 店舗別の情報(更新用)を作成する inventoryShipmentPackingStoreItemDtoArr.push({ itemNumber: location.state.shipmentItems[itemIndex].itemNumber, shipmentOrderNumber: "", shipmentPackingItemOrderNumber: "", chainCorporateCode: "", packingChainStoreCode: store.packingChainStoreCode, storeName: store.storeName, numberOfOrders: inventoryShipmentPackingStoreItemDtoWorkArr[storeIndex] ?.numberOfOrders || 0, numberOfShipments: inventoryShipmentPackingStoreItemDtoWorkArr[storeIndex] ?.numberOfShipments || 0, }); }); // 選択されてる明細行の情報を引き続く let itemNumber: number = location.state.shipmentItems[itemIndex].itemNumber; let shipmentOrderNumber: string = itemIndex === selectedRowIndex ? item?.shipmentOrderNumber : ""; let shipmentPackingItemOrderNumber: string = itemIndex === selectedRowIndex ? item?.shipmentPackingItemOrderNumber : ""; return { itemNumber: itemNumber, shipmentOrderNumber: shipmentOrderNumber, shipmentPackingItemOrderNumber: shipmentPackingItemOrderNumber, numberOfOrders: totalStoreNumberOfOrders, numberOfShipments: totalStoreNumberOfShipments, inventoryShipmentPackingStoreItemDtoArr: inventoryShipmentPackingStoreItemDtoArr, }; }); console.log("initialShipmentItems"); console.dir(initialShipmentItems); // shipmentItemsを更新する //shipmentItems = initialShipmentItems; setShipmentItems( initialShipmentItems.map((shipmentItem, index) => { return { ...shipmentItem, numberOfOrders: shipmentItem.numberOfOrders, numberOfShipments: shipmentItem.numberOfShipments, inventoryShipmentPackingStoreItemDtoArr: shipmentItem.inventoryShipmentPackingStoreItemDtoArr.map( (store, storeIndex) => { return { ...store, storeName: store.storeName, packingChainStoreCode: store.packingChainStoreCode, numberOfOrders: store.numberOfOrders, numberOfShipments: store.numberOfShipments, }; } ), }; }) ); initShipmentItems = initialShipmentItems; console.log("【useEffect】【end】初回実行"); //setIsInit(true); }, []); // shipmentItemsが更新された後に実行される処理 React.useEffect(() => { console.log("【useEffect】【start】shipmentItemsの更新"); console.log("setShipmentItems(initialShipmentItems);が実行されました。"); console.log("shipmentItems"); console.dir(shipmentItems); console.log("initShipmentItems"); console.dir(initShipmentItems); // <tbody>の部分を作成する //tableBody = renderFormFields(); //setIsRender(true); console.log("isAready"); //console.dir(isRender); console.log("tableBody"); console.dir(tableBody); console.dir(typeof tableBody); tableBody.map((element: JSX.Element) => { console.dir(React.isValidElement(element)); }); // レンダリングの準備ができた setIsReadyRender(true); // if (isInit) { // console.log("tbody更新"); // console.log("updated"); // const tbody = document.getElementById("tbody"); // if (tbody) { // const root = createRoot(tbody); // root.render(renderFormFields()); // } // } console.log("【useEffect】【end】shipmentItemsの更新"); }, [shipmentItems]); // <input>の値が変更されたら実施 const handleInputChange = ( selectedItemIndex: number, event: React.ChangeEvent<HTMLInputElement> ) => { event.preventDefault(); const { name, value } = event.target; console.log("name"); console.log(name); console.log("value"); console.log(value); let propertyName = name.split("_")[0]; const copyForUpdateShipments = JSON.parse(JSON.stringify(shipmentItems)); const updateShipments: IInventoryShipmentPackingItemDto[] = copyForUpdateShipments.map((item: IInventoryShipmentPackingItemDto, index: number) => { // 店舗別の情報を更新 const workIInventoryShipmentPackingStoreItemDtoArr = item.inventoryShipmentPackingStoreItemDtoArr.map((store: IInventoryShipmentPackingStoreItemDto, storeIndex: number) => { if (storeIndex === selectedItemIndex) { return { ...store, numberOfOrders: propertyName === 'numberOfOrders'? value: store.numberOfOrders, numberOfShipments: propertyName === 'numberOfShipments'? value: store.numberOfShipments } } else { return { ...store } } }); return { ...item, inventoryShipmentPackingStoreItemDtoArr: workIInventoryShipmentPackingStoreItemDtoArr } }); setShipmentItems(updateShipments); // shipmentItems = updatedShipmentItems; //renderFormFields(); }; console.log("handleInputChange■shipmentItems"); console.dir(shipmentItems); console.dir(shipmentItems[selectedRowIndex]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // フォームの送信処理 }; // <tbody>部分を作成する処理 const renderFormFields = () => { const formFields: JSX.Element[] = []; console.log("renderFormFields■shipmentItems"); console.dir(shipmentItems[selectedRowIndex]); { /* 店舗の数だけ繰り返し */ } for (let i = 0; i < storeData.length; i += 4) { const rowFields: JSX.Element[] = []; { /* 1行に4店舗 */ } for (let j = i; j < i + 4; j++) { if (j >= storeData.length) { break; } let itemIndex = j; rowFields.push( <td> <div style={{ display: "flex" }}> <div style={{ minWidth: "128px" }}> {/* 梱包対象チェーン店舗コード */} <div> <span> { shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `packingChainStoreCode` ] } </span> <input style={{ width: "100%", boxSizing: "border-box" }} type="hidden" id={`packingChainStoreCode_${itemIndex}`} key={`packingChainStoreCode_${itemIndex}`} name={`packingChainStoreCode_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `packingChainStoreCode` ] } /> </div> {/* 店舗名 */} <div> <span> { shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `storeName` ] } </span> <input style={{ width: "100%", boxSizing: "border-box" }} type="hidden" id={`storeName_${itemIndex}`} key={`storeName_${itemIndex}`} name={`storeName_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `storeName` ] } /> </div> </div> <div> {/* 店舗別発注数 */} <div> <input style={{ width: "100%", boxSizing: "border-box" }} type="text" id={`numberOfOrders_${itemIndex}`} key={`numberOfOrders_${itemIndex}`} name={`numberOfOrders_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `numberOfOrders` || '' ] } onChange={(e) => handleInputChange(itemIndex, e)} /> </div> {/* 店舗別出荷数 */} <div> <input style={{ width: "100%", boxSizing: "border-box" }} type="text" id={`numberOfShipments_${itemIndex}`} key={`numberOfShipments_${itemIndex}`} name={`numberOfShipments_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `numberOfShipments` || '' ] } onChange={(e) => handleInputChange(itemIndex, e)} /> </div> </div> </div> </td> ); } formFields.push(<tr key={i}>{rowFields}</tr>); } return formFields; }; // <thead>部分を作成する処理 const renderTableHeaders = () => { const headers: JSX.Element[] = []; const cells: JSX.Element[] = []; for (let i = 0; i < 4; i++) { cells.push( <th key={i}> {/* 適切なヘッダーテキストを表示 */} <div style={{ display: "flex" }}> <div style={{ minWidth: "128px" }}> <span>店舗コード</span> <br /> <span>店舗名</span> </div> <div style={{ minWidth: "128px" }}> <span>店舗別発注数</span> <br /> <span>店舗別出荷数</span> </div> </div> </th> ); } headers.push(<tr key={0}>{cells}</tr>); return headers; }; // const onPageLoaded = () => { // setOnloaded(true); // }; // if (document.readyState === "complete") { // if (!onloaded) { // () => onPageLoaded(); // } // } else { // window.addEventListener("load", onPageLoaded); // window.removeEventListener("load", onPageLoaded); // } // // 初期化が済んでいれば実施 // useEffect(() => { // const tbody = document.getElementById("tbody"); // console.log("tbody"); // console.dir(tbody); // if (tbody && tbody.childNodes.length === 0) { // console.log("dom loaded"); // console.log("レンダリング時:shipmentItems"); // console.dir(shipmentItems); // const root = createRoot(tbody); // root.render(tableBody); // // tableBody.map((element) => { // // root.render(element); // // }); // //setOnloaded(false); // } // }, [isInit]); // 画面の描画 return ( <div> {/* {isRender ? (*/} {isReadyRender ? ( <> <h2>Page 2</h2> <form onSubmit={handleSubmit}> <table> <thead>{renderTableHeaders()}</thead> <tbody id="tbody">{renderFormFields()}</tbody> </table> <div> <button type="submit">次へ</button> <button type="button" onClick={() => navigate(-1)}> 戻る </button> </div> </form> </> ) : ( <> <p>...loading</p> </> )} </div> ); }; export default Page2;
【変更後】my-react-spa-app\src\page\shipment\create\page2.tsx
import React, { useContext, useEffect, useState } from "react"; //import { render } from 'react-dom'; import { createRoot } from "react-dom/client"; import { useNavigate, useLocation } from "react-router-dom"; import { ShipmentItemsContext } from "./context"; import { IInventoryShipmentPackingItemDto } from "../../../components/model/dto/shipment/inventory-shipment-packing-item-dto"; import { IInventoryShipmentPackingStoreItemDto } from "../../../components/model/dto/shipment/inventory-shipment-packing-store-item-dto"; const Page2: React.FC = () => { const location = useLocation(); const navigate = useNavigate(); const shipmentItemsContext = useContext(ShipmentItemsContext); console.log("page2■ShipmentItemsContext"); console.dir(shipmentItemsContext); // const [isRender, setIsRender] = useState(true); // const [onloaded, setOnloaded] = useState(false); // const [isInit, setIsInit] = useState(false); const [isReadyRender, setIsReadyRender] = useState(false); if (!shipmentItemsContext) { // ShipmentItemsContextが未定義の場合のエラーハンドリング return null; } // 選択された行の番号 const selectedRowIndex = location.state?.selectedRowIndex || 0; // let stateShipmetItemArr: IInventoryShipmentPackingItemDto[] = location.state?.shipmentItems || []; console.log("location.state"); console.dir(location.state); //let [shipmentItems, setShipmentItems] = useState< // const [shipmentItems, setShipmentItems] = useState< // IInventoryShipmentPackingItemDto[] // >( // location.state && location.state.shipmentItems // ? location.state.shipmentItems // : [] // ); const { shipmentItems, setShipmentItems }: any = useContext(ShipmentItemsContext); console.log("shipmentItemsContext"); console.dir(shipmentItemsContext); // サーバーサイドからデータが取得された想定 let initShipmentItems: any = []; let tableBody: JSX.Element[] = [] as JSX.Element[]; let storeData = [ { packingChainStoreCode: "01", storeName: "保谷店" }, { packingChainStoreCode: "02", storeName: "大泉学園店" }, { packingChainStoreCode: "03", storeName: "練馬駅前店" }, { packingChainStoreCode: "04", storeName: "石神井公園店" }, { packingChainStoreCode: "05", storeName: "石神井台店" }, { packingChainStoreCode: "06", storeName: "練馬店" }, { packingChainStoreCode: "07", storeName: "中村橋店" }, { packingChainStoreCode: "08", storeName: "成増店" }, { packingChainStoreCode: "09", storeName: "田無店" }, { packingChainStoreCode: "10", storeName: "東中野店" }, { packingChainStoreCode: "11", storeName: "高円寺店" }, { packingChainStoreCode: "12", storeName: "笹塚店" }, { packingChainStoreCode: "13", storeName: "上池袋店" }, { packingChainStoreCode: "14", storeName: "東新宿店" }, { packingChainStoreCode: "15", storeName: "新宿店" }, { packingChainStoreCode: "16", storeName: "中野店" }, { packingChainStoreCode: "17", storeName: "三鷹店" }, { packingChainStoreCode: "18", storeName: "武蔵境店" }, { packingChainStoreCode: "19", storeName: "吉祥寺店" }, { packingChainStoreCode: "20", storeName: "西荻窪店" }, ]; React.useEffect(() => { const isAreadyInit: boolean = shipmentItems.every((item: IInventoryShipmentPackingItemDto) => item.inventoryShipmentPackingStoreItemDtoArr.length !== 0) if (isAreadyInit) { return; } console.log("【useEffect】【start】初回実行"); // 選択された行の情報を取得 // const selectedRow = location.state.shipmentItems[selectedRowIndex]; const copyShipmentItems = JSON.parse(JSON.stringify(shipmentItems)); console.log("copyShipmentItems"); console.dir(copyShipmentItems); const initialShipmentItems: IInventoryShipmentPackingItemDto[] = //location.state.shipmentItems.map((item: any, itemIndex: number) => { copyShipmentItems.map((item: any, itemIndex: number) => { // 店舗別の情報(更新用) let inventoryShipmentPackingStoreItemDtoArr: IInventoryShipmentPackingStoreItemDto[] = []; // 店舗別発注数合計 let totalStoreNumberOfOrders = 0; // 店舗別出荷数合計 let totalStoreNumberOfShipments = 0; // 店舗別の情報(作業用) const inventoryShipmentPackingStoreItemDtoWorkArr = item[selectedRowIndex]?.inventoryShipmentPackingStoreItemDtoArr || []; // サーバーからのデータを設定していく storeData.forEach((store: any, storeIndex: number) => { // 店舗別の情報 // const selected = inventoryShipmentPackingStoreItemDtoWorkArr.some( // (storeItem: any) => // storeItem?.packingChainStoreCode === store.packingChainStoreCode // ); // 選択されてる行の場合だけ実施 if (itemIndex === selectedRowIndex) { // 店舗別の発注数、出荷数の合計を算出する item.inventoryShipmentPackingStoreItemDtoArr.forEach( (itemStore: any, itemStoreIndex: number) => { // 発注数の合計 if (Number.isInteger(itemStore.numberOfOrders)) { totalStoreNumberOfOrders += itemStore.numberOfOrders; } // 出荷数の合計 if (Number.isInteger(itemStore.numberOfShipments)) { totalStoreNumberOfShipments += itemStore.numberOfShipments; } } ); } // 店舗別の情報(更新用)を作成する inventoryShipmentPackingStoreItemDtoArr.push({ itemNumber: location.state.shipmentItems[itemIndex].itemNumber, shipmentOrderNumber: "", shipmentPackingItemOrderNumber: "", chainCorporateCode: "", packingChainStoreCode: store.packingChainStoreCode, storeName: store.storeName, numberOfOrders: inventoryShipmentPackingStoreItemDtoWorkArr[storeIndex] ?.numberOfOrders || 0, numberOfShipments: inventoryShipmentPackingStoreItemDtoWorkArr[storeIndex] ?.numberOfShipments || 0, }); }); // 選択されてる明細行の情報を引き続く let itemNumber: number = location.state.shipmentItems[itemIndex].itemNumber; let shipmentOrderNumber: string = itemIndex === selectedRowIndex ? item?.shipmentOrderNumber : ""; let shipmentPackingItemOrderNumber: string = itemIndex === selectedRowIndex ? item?.shipmentPackingItemOrderNumber : ""; return { itemNumber: itemNumber, shipmentOrderNumber: shipmentOrderNumber, shipmentPackingItemOrderNumber: shipmentPackingItemOrderNumber, numberOfOrders: totalStoreNumberOfOrders, numberOfShipments: totalStoreNumberOfShipments, inventoryShipmentPackingStoreItemDtoArr: inventoryShipmentPackingStoreItemDtoArr, }; }); console.log("initialShipmentItems"); console.dir(initialShipmentItems); // shipmentItemsを更新する //shipmentItems = initialShipmentItems; setShipmentItems( initialShipmentItems.map((shipmentItem, index) => { return { ...shipmentItem, numberOfOrders: shipmentItem.numberOfOrders, numberOfShipments: shipmentItem.numberOfShipments, inventoryShipmentPackingStoreItemDtoArr: shipmentItem.inventoryShipmentPackingStoreItemDtoArr.map( (store, storeIndex) => { return { ...store, storeName: store.storeName, packingChainStoreCode: store.packingChainStoreCode, numberOfOrders: store.numberOfOrders, numberOfShipments: store.numberOfShipments, }; } ), }; }) ); initShipmentItems = initialShipmentItems; console.log("【useEffect】【end】初回実行"); //setIsInit(true); }, []); // shipmentItemsが更新された後に実行される処理 React.useEffect(() => { console.log("【useEffect】【start】shipmentItemsの更新"); console.log("setShipmentItems(initialShipmentItems);が実行されました。"); console.log("shipmentItems"); console.dir(shipmentItems); console.log("initShipmentItems"); console.dir(initShipmentItems); // <tbody>の部分を作成する //tableBody = renderFormFields(); //setIsRender(true); console.log("isAready"); //console.dir(isRender); console.log("tableBody"); console.dir(tableBody); console.dir(typeof tableBody); tableBody.map((element: JSX.Element) => { console.dir(React.isValidElement(element)); }); // レンダリングの準備ができた setIsReadyRender(true); // if (isInit) { // console.log("tbody更新"); // console.log("updated"); // const tbody = document.getElementById("tbody"); // if (tbody) { // const root = createRoot(tbody); // root.render(renderFormFields()); // } // } console.log("【useEffect】【end】shipmentItemsの更新"); }, [shipmentItems]); // <input>の値が変更されたら実施 const handleInputChange = ( selectedItemIndex: number, event: React.ChangeEvent<HTMLInputElement> ) => { event.preventDefault(); const { name, value } = event.target; console.log("name"); console.log(name); console.log("value"); console.log(value); let propertyName = name.split("_")[0]; const copyForUpdateShipments = JSON.parse(JSON.stringify(shipmentItems)); const updateShipments: IInventoryShipmentPackingItemDto[] = copyForUpdateShipments.map((item: IInventoryShipmentPackingItemDto, index: number) => { // 店舗別の情報を更新 const workIInventoryShipmentPackingStoreItemDtoArr = item.inventoryShipmentPackingStoreItemDtoArr.map((store: IInventoryShipmentPackingStoreItemDto, storeIndex: number) => { if (storeIndex === selectedItemIndex) { return { ...store, numberOfOrders: propertyName === 'numberOfOrders'? value: store.numberOfOrders, numberOfShipments: propertyName === 'numberOfShipments'? value: store.numberOfShipments } } else { return { ...store } } }); return { ...item, inventoryShipmentPackingStoreItemDtoArr: workIInventoryShipmentPackingStoreItemDtoArr } }); setShipmentItems(updateShipments); // shipmentItems = updatedShipmentItems; //renderFormFields(); }; console.log("handleInputChange■shipmentItems"); console.dir(shipmentItems); console.dir(shipmentItems[selectedRowIndex]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // フォームの送信処理 }; // <tbody>部分を作成する処理 const renderFormFields = () => { const formFields: JSX.Element[] = []; console.log("renderFormFields■shipmentItems"); console.dir(shipmentItems[selectedRowIndex]); { /* 店舗の数だけ繰り返し */ } for (let i = 0; i < storeData.length; i += 4) { const rowFields: JSX.Element[] = []; { /* 1行に4店舗 */ } for (let j = i; j < i + 4; j++) { if (j >= storeData.length) { break; } let itemIndex = j; rowFields.push( <td> <div style={{ display: "flex" }}> <div style={{ minWidth: "128px" }}> {/* 梱包対象チェーン店舗コード */} <div> <span> { shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `packingChainStoreCode` ] } </span> <input style={{ width: "100%", boxSizing: "border-box" }} type="hidden" id={`packingChainStoreCode_${itemIndex}`} key={`packingChainStoreCode_${itemIndex}`} name={`packingChainStoreCode_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `packingChainStoreCode` || '' ] } /> </div> {/* 店舗名 */} <div> <span> { shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `storeName` ] } </span> <input style={{ width: "100%", boxSizing: "border-box" }} type="hidden" id={`storeName_${itemIndex}`} key={`storeName_${itemIndex}`} name={`storeName_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `storeName` || '' ] } /> </div> </div> <div> {/* 店舗別発注数 */} <div> <input style={{ width: "100%", boxSizing: "border-box" }} type="text" id={`numberOfOrders_${itemIndex}`} key={`numberOfOrders_${itemIndex}`} name={`numberOfOrders_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `numberOfOrders` || '' ] } onChange={(e) => handleInputChange(itemIndex, e)} /> </div> {/* 店舗別出荷数 */} <div> <input style={{ width: "100%", boxSizing: "border-box" }} type="text" id={`numberOfShipments_${itemIndex}`} key={`numberOfShipments_${itemIndex}`} name={`numberOfShipments_${itemIndex}`} value={ shipmentItems[selectedRowIndex] ?.inventoryShipmentPackingStoreItemDtoArr[itemIndex]?.[ `numberOfShipments` || '' ] } onChange={(e) => handleInputChange(itemIndex, e)} /> </div> </div> </div> </td> ); } formFields.push(<tr key={i}>{rowFields}</tr>); } return formFields; }; // <thead>部分を作成する処理 const renderTableHeaders = () => { const headers: JSX.Element[] = []; const cells: JSX.Element[] = []; for (let i = 0; i < 4; i++) { cells.push( <th key={i}> {/* 適切なヘッダーテキストを表示 */} <div style={{ display: "flex" }}> <div style={{ minWidth: "128px" }}> <span>店舗コード</span> <br /> <span>店舗名</span> </div> <div style={{ minWidth: "128px" }}> <span>店舗別発注数</span> <br /> <span>店舗別出荷数</span> </div> </div> </th> ); } headers.push(<tr key={0}>{cells}</tr>); return headers; }; // const onPageLoaded = () => { // setOnloaded(true); // }; // if (document.readyState === "complete") { // if (!onloaded) { // () => onPageLoaded(); // } // } else { // window.addEventListener("load", onPageLoaded); // window.removeEventListener("load", onPageLoaded); // } // // 初期化が済んでいれば実施 // useEffect(() => { // const tbody = document.getElementById("tbody"); // console.log("tbody"); // console.dir(tbody); // if (tbody && tbody.childNodes.length === 0) { // console.log("dom loaded"); // console.log("レンダリング時:shipmentItems"); // console.dir(shipmentItems); // const root = createRoot(tbody); // root.render(tableBody); // // tableBody.map((element) => { // // root.render(element); // // }); // //setOnloaded(false); // } // }, [isInit]); // 画面の描画 return ( <div> {/* {isRender ? (*/} {isReadyRender ? ( <> <h2>Page 2</h2> <form onSubmit={handleSubmit}> <table> <thead>{renderTableHeaders()}</thead> <tbody id="tbody">{renderFormFields()}</tbody> </table> <div> <button type="submit">次へ</button> <button type="button" onClick={() => navigate(-1)}> 戻る </button> </div> </form> </> ) : ( <> <p>...loading</p> </> )} </div> ); }; export default Page2;
⇧ と、<input>のvalue属性にundefinedが設定されないようにしたところ、
警告が消えました。
React、独自のルールが多過ぎて、辛い...
毎度モヤモヤ感が半端ない...
今回はこのへんで。