วิธีการกดใน React Hooks (useState)?


คำตอบ:


312

เมื่อคุณใช้useStateคุณจะได้รับวิธีการอัปเดตสำหรับรายการสถานะ:

const [theArray, setTheArray] = useState(initialArray);

จากนั้นเมื่อคุณต้องการเพิ่มองค์ประกอบใหม่คุณจะใช้ฟังก์ชันนั้นและส่งผ่านอาร์เรย์ใหม่หรือฟังก์ชันที่จะสร้างอาร์เรย์ใหม่ โดยปกติอย่างหลังเนื่องจากการอัปเดตสถานะเป็นแบบอะซิงโครนัสและบางครั้งเป็นกลุ่ม:

setTheArray(oldArray => [...oldArray, newElement]);

บางครั้งคุณจะได้รับไปโดยไม่ต้องใช้รูปแบบการโทรกลับว่าถ้าคุณเพียงอัปเดตในอาร์เรย์ขนย้ายวัสดุสำหรับการจัดกิจกรรมของผู้ใช้บางอย่างที่เฉพาะเจาะจงเช่นclick( แต่ไม่ชอบmousemove):

setTheArray([...theArray, newElement]);

เหตุการณ์ที่เกิดขึ้นซึ่งตอบสนองเพื่อให้แน่ใจว่าการแสดงผลเป็นแดงคือ "เหตุการณ์ที่เกิดขึ้นต่อเนื่อง" ที่ระบุไว้ที่นี่

ตัวอย่างสด (ส่งการโทรกลับไปที่setTheArray):

เพราะการปรับปรุงเท่านั้นที่จะtheArrayอยู่ในนั้นเป็นหนึ่งในclickเหตุการณ์ (หนึ่งในกิจกรรม "ต่อเนื่อง") ผมได้รับไปกับการปรับปรุงโดยตรงในaddEntry:


1
... และลองsetTheArray(currentArray => [...currentArray, newElement])หากข้างต้นไม่ได้ผล ส่วนใหญ่มีแนวโน้มในuseEffect(...theArray..., [])มันเป็นสิ่งสำคัญที่จะตระหนักว่าtheArrayเป็นค่าคงที่ดังนั้นการปรับปรุงการทำงานมีความจำเป็นสำหรับค่าจากอนาคตวาทกรรม
Aprillion

@Aprillion - ไม่ค่อยแน่ใจว่าคุณพูดอะไรกับuseEffectตัวอย่างนั้น... ?
TJ Crowder

1
หากuseEffectมีรายการอ้างอิงว่างรายการ[]จะถูกดำเนินการในระหว่างการแสดงผลครั้งแรกเท่านั้น ดังนั้นค่าของภายในผลจะเป็นtheArray initialArrayในสถานการณ์ที่setTheArray([...initialArray, newElement])ไม่สมเหตุสมผลและเมื่อtheArrayค่าคงที่จะเท่ากับเสมอinitialValueจึงจำเป็นต้องมีการอัปเดตการทำงาน
Aprillion

@Aprillion - กลัวว่าจะยังไม่ได้รับอาจจะหนาแน่นไปหน่อย :-) เหตุใดคุณจึงต้องการเอฟเฟกต์ด้วยค่าอาร์เรย์เริ่มต้น (ฉันหมายความว่าอาจมีกรณีการใช้งานที่ไม่บ่อยนัก แต่ ... ) และถึงคุณจะทำยังไงกับคำถาม?
TJ Crowder

1
ผู้ชายฉันต่อสู้กับสิ่งนี้มานานที่สุด ฉันคิดว่าsetTheArray(()=>theArray.concat(newElement))จะได้ผล แต่ไม่! พารามิเตอร์ที่เรียกกลับทำให้เกิดความแตกต่างทั้งหมด
Jeff Lowery

52

หากต้องการขยายเพิ่มเติมเล็กน้อยต่อไปนี้เป็นตัวอย่างทั่วไป เริ่มต้นด้วย:

const [theArray, setTheArray] = useState(initialArray);
const [theObject, setTheObject] = useState(initialObject);

ผลักดันองค์ประกอบที่ส่วนท้ายของอาร์เรย์

setTheArray(prevArray => [...prevArray, newValue])

พุช / อัปเดตองค์ประกอบที่ส่วนท้ายของวัตถุ

setTheObject(prevState => ({ ...prevState, currentOrNewKey: newValue}));

พุช / อัปเดตองค์ประกอบที่ส่วนท้ายของอาร์เรย์ของวัตถุ

setTheArray(prevState => [...prevState, {currentOrNewKey: newValue}]);

ผลักดันองค์ประกอบที่ส่วนท้ายของวัตถุของอาร์เรย์

let specificArrayInObject = theObject.array.slice();
specificArrayInObject.push(newValue);
const newObj = { ...theObject, [event.target.name]: specificArrayInObject };
theObject(newObj);

นี่คือตัวอย่างการทำงานบางส่วนด้วย https://codesandbox.io/s/reacthooks-push-r991u


5
ขอบคุณสำหรับทุกตัวอย่าง สิ่งที่แน่นอนผมมีปัญหากับคุณเป็นองค์ประกอบผลักดันในตอนท้ายของวัตถุของอาร์เรย์ ฉันลองทำสิ่งต่างๆมากมาย แต่ก็ไม่สามารถรวมเข้าด้วยกันได้
sdouble

1
การใช้ prevState แก้ไขปัญหาของฉันที่มีการใช้เฉพาะการโทรครั้งสุดท้ายบนการต่ออาร์เรย์และตัวกรองพร้อมกัน
Henrique Bruno

5

เช่นเดียวกับที่คุณทำกับสถานะ "ปกติ" ในส่วนประกอบของคลาส React

ตัวอย่าง:

function App() {
  const [state, setState] = useState([]);

  return (
    <div>
      <p>You clicked {state.join(" and ")}</p>
      //destructuring
      <button onClick={() => setState([...state, "again"])}>Click me</button>
      //old way
      <button onClick={() => setState(state.concat("again"))}>Click me</button>
    </div>
  );
}

1
สิ่งนี้จะใช้ได้กับ onClick แต่ setState (oldState => [... oldState, pushsomething]) เป็นวิธีที่คุณควรทำ
Cyrus Zei

3
// Save search term state to React Hooks with spread operator and wrapper function

// Using .concat(), no wrapper function (not recommended)
setSearches(searches.concat(query))

// Using .concat(), wrapper function (recommended)
setSearches(searches => searches.concat(query))

// Spread operator, no wrapper function (not recommended)
setSearches([...searches, query])

// Spread operator, wrapper function (recommended)
setSearches(searches => [...searches, query])

https://medium.com/javascript-in-plain-english/how-to-add-to-an-array-in-react-state-3d08ddb2e1dc


2

setTheArray([...theArray, newElement]);เป็นคำตอบที่ง่ายที่สุด แต่ต้องระวังสำหรับการกลายพันธุ์ของรายการในtheArray ใช้การโคลนไอเท็มอาร์เรย์แบบลึก


การโคลนลึกที่คุณพูดถึงหมายความว่ารับเป็น objA หรือไม่ UseState และต่อ objA กับ objB? เหมือนกับลิงค์นี้ไหม gist.githubusercontent.com/kahsing/…
Luiey

1

วิธีที่แนะนำมากที่สุดคือการใช้ฟังก์ชัน wrapper และตัวดำเนินการกระจายร่วมกัน ตัวอย่างเช่นหากคุณเริ่มต้นสถานะที่เรียกnameเช่นนี้

const [names, setNames] = useState([])

คุณสามารถกดไปที่อาร์เรย์นี้ได้เช่นนี้

setNames(names => [...names, newName])

หวังว่าจะช่วยได้


1
โปรดอย่าโพสต์คำตอบแบบลิงก์เท่านั้น ลิงก์อาจเสียในอนาคต ให้เขียนบทความนั้นขึ้นมาแทนและตรวจสอบให้แน่ใจว่าได้ตอบคำถามนั้นจริง
BlackCetha

แม้ว่าลิงก์นี้อาจตอบคำถามได้ แต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์เพื่อการอ้างอิง คำตอบแบบลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าที่เชื่อมโยงเปลี่ยนไป - จากรีวิว
ilke444

1

หากคุณต้องการผลักดันหลังจากดัชนีเฉพาะคุณสามารถทำได้ดังนี้:

   const handleAddAfterIndex = index => {
       setTheArray(oldItems => {
            const copyItems = [...oldItems];
            const finalItems = [];
            for (let i = 0; i < copyItems.length; i += 1) {
                if (i === index) {
                    finalItems.push(copyItems[i]);
                    finalItems.push(newItem);
                } else {
                    finalItems.push(copyItems[i]);
                }
            }
            return finalItems;
        });
    };

1

คุณสามารถต่อท้ายอาร์เรย์ของข้อมูลที่ส่วนท้ายของสถานะที่กำหนดเอง:

  const [vehicleData, setVehicleData] = React.useState<any[]>([]);
  setVehicleData(old => [...old, ...newArrayData]);

ตัวอย่างเช่นด้านล่างนี้คุณจะปรากฏตัวอย่างของ axios:

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        {
          url: `http://localhost:4000/api/vehicle?page=${page + 1}&pageSize=10`,
          method: 'get',
        }
      );
      setVehicleData(old => [...old, ...result.data.data]);
    };

    fetchData();
  }, [page]);

0

ฉันลองใช้วิธีการข้างต้นในการผลักวัตถุเข้าไปในอาร์เรย์ของวัตถุใน useState แต่มีข้อผิดพลาดต่อไปนี้เมื่อใช้TypeScript :

พิมพ์ 'TxBacklog [] | ไม่ได้กำหนด 'ต้องมีเมธอด' Symbol.iterator 'ที่ส่งคืน iterator.ts (2488)

เห็นได้ชัดว่าการตั้งค่าสำหรับ tsconfig.json นั้นถูกต้อง:

{
   "compilerOptions": {
   "target": "es6",
   "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es6",
],

วิธีแก้ปัญหานี้ช่วยแก้ปัญหาได้ (รหัสตัวอย่างของฉัน):

อินเตอร์เฟซ:

   interface TxBacklog {
      status: string,
      txHash: string,
   }

ตัวแปรสถานะ:

    const [txBacklog, setTxBacklog] = React.useState<TxBacklog[]>();

ผลักวัตถุใหม่ลงในอาร์เรย์:

    // Define new object to be added
    const newTx = {
       txHash: '0x368eb7269eb88ba86..',
       status: 'pending'
    };
    // Push new object into array
    (txBacklog) 
       ? setTxBacklog(prevState => [ ...prevState!, newTx ])
       : setTxBacklog([newTx]);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.