0

I have the following class:

const NewPage = () => {
    const [blocks, setBlocks] = useState(false);
    const [needsShowImageModal, setNeedsShowImageModal] = useState(false);

    const textButtonHandler = () => {
        const key = randomInt(0, 1000000000);
        setBlocks([
            ...blocks, 
            <TextBlock 
                key={key}
                blockKey={key}
                deleteButtonHandler={deleteButtonHandler} 
            />
        ]);
        console.log(blocks);
    };
    return (.....);
}

For some reason the first time textButtonHandler is called it doesn't add the item to the array. I have checked the length of the array in the console and it is 0. What am I doing wrong here?

EDIT: After suggestions I now have:

const NewPage = () => {
    const [blocks, setBlocks] = useState([]);
    const [needsShowImageModal, setNeedsShowImageModal] = useState([]);

    const textButtonHandler = () => {
        const key = randomInt(0, 1000000000);
        setBlocks([
            ...blocks,
            { key, deleteButtonHandler}
        ]);
    };

    const deleteButtonHandler = (blockKey) => {
        console.log(blocks); // Empty
    };

    return (
        <div>
            <ImageModal 
                show={needsShowImageModal}
                onHide={() => setNeedsShowImageModal(false)}
                insertButtonHandler={insertImageHandler}
            />
            <div className="d-flex">
                <NewPageSidebar
                    textButtonHandler={textButtonHandler}
                    imageButtonHandler={imageButtonHandler}
                    spacingButtonHandler={spacingButtonHandler}
                />
                <NewPageContent blocks={blocks} />
            </div>
        </div>
    );
};

export default NewPage;

However if I add a block using textButtonHandler and then fire deleteButtonHandler the array is still empty.

Kex
  • 8,023
  • 9
  • 56
  • 129
  • 3
    Why are you trying to store component inside array? This is wrong approach. And setState is asynchronous function. Move console.log outside the function – demkovych Jun 24 '20 at 09:04
  • I don t think its a wrong approach. You can do that any time – Peter Jun 24 '20 at 09:05
  • Building array of components is certainly something you do in React,. How are you using textButtonHandler?, your likely hitting a scope issue, try using the callback version of setState -> `setBlocks(blocks => .....)` – Keith Jun 24 '20 at 09:07
  • 1
    yeah i think react advised not to store component in state, can't find link – Giorgi Moniava Jun 24 '20 at 09:09
  • @Keith textButtonHandler is the handler function for a child component in NewPage, just passed as a prop – Kex Jun 24 '20 at 09:09
  • 1
    @giorgim Oh, yeah.. see what you mean now. Ideally you would setState the values for your render, not the components themselves. – Keith Jun 24 '20 at 09:12
  • But to store component in array and in state, its a different issue. I am also not fun of store component in state, but iI have had an issue when i needed to do that. Other case You are able to store Component in array. `retur [

    h1 element

    ,< key="h2" h2>h2 element];`
    – Peter Jun 24 '20 at 09:18
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Drew Reese Jun 24 '20 at 09:19
  • Keith has right, You should manage the state at that way. setBlocks(prevBlocks => prevBlocks.concat( – Peter Jun 24 '20 at 09:25
  • I wasn't aware that components in state is not recomended. Does seen weird actually. I guess I should just use a data model instead, keep the data models in a state array and then set components in render – Kex Jun 24 '20 at 09:27
  • I changed to using a data model and the first element in the array is still not present. Please see the edit to the question. – Kex Jun 24 '20 at 10:50

1 Answers1

0

1- you should add component in array by name and not wrap it up like < ../> and when using it in jsx you can do that, so your object can be like this:

{
   key:key,
   deleteButtonHandler:deleteButtonHandler,
   comp:TextBlock
}

2- if all of object in blocks have the same element, you don't need to have array of elements, array of object with only info for comp is enough

3-suggested solution:

const NewPage = () => {
    const [blocks, setBlocks] = useState([]);
    const [needsShowImageModal, setNeedsShowImageModal] = useState(false);

    const textButtonHandler = () => {
        const key = randomInt(0, 1000000000);
        setBlocks([
            ...blocks, 
           {
             key:key,
             deleteButtonHandler:deleteButtonHandler
           }
        ]);
        console.log(blocks);
    };
    return (<div> {blocks.map((info)=>(...))} </div>);
}

in above code after creating list of blocks as info holder you can use it by .map like example.

  • I tried this and the first element is still not present – Kex Jun 24 '20 at 10:45
  • i don't see any problem in what you wrote, maybe a repo to your full source code could help, you cant test the senario in small program as i did and see it would work! – shahrooz bazrafshan Jun 24 '20 at 11:11
  • and maybe the problem is in witch comp and how you fire deleteButtonHandler – shahrooz bazrafshan Jun 24 '20 at 11:12
  • Why would the component make a difference? A button fire is just a button fire no? State has already been set, async code finished etc. – Kex Jun 24 '20 at 11:54
  • I noticed something. If I add more than one block when I tap the button for that block it gives the count of the array before the block was created. e.g. If I tap button 3 it gives me the count as 2. It's almost like it took a snapshot and it isn't generating the length correctly.. – Kex Jun 24 '20 at 12:07