Javascript 将函数作为道具传递,调用该函数时,它不会';无法获得正确的值

Javascript 将函数作为道具传递,调用该函数时,它不会';无法获得正确的值,javascript,reactjs,function,react-props,Javascript,Reactjs,Function,React Props,我在React应用程序中将函数作为道具传递时遇到了一个奇怪的问题。代码如下: const NewPage = () => { const [blocks, setBlocks] = useState([]); const [needsShowImageModal, setNeedsShowImageModal] = useState(false); const textButtonHandler = () => { const key = r

我在React应用程序中将函数作为道具传递时遇到了一个奇怪的问题。代码如下:

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

    const textButtonHandler = () => {
        const key = randomInt(0, 1000000000);
        const array = blocks.concat({ key, deleteButtonHandler: deleteButtonHandler });
        setBlocks(array);
    };

    function deleteButtonHandler(blockKey) {
        // Test functionality, if one text field was added arrray size should
        // be 1
        console.log(blocks.length);
    }
    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;
最后,这个处理程序被传递到
TextBlock

const TextBlock = ({ deleteButtonHandler, blockKey }) => {
    const [editorState, setEditorState] = useState(
        () => EditorState.createEmpty(),
    );

    const toolbarClickHander = (buttonType, e) => {
        e.preventDefault();
        switch (buttonType) {
            case 'delete':
                // Delete button handler called here
                deleteButtonHandler(blockKey);
                break;
            default:
                break;
        }
    };

    return(
        <div className='text-block'>
            <TextBlockToolbar clickHandler={toolbarClickHander} />
             <Editor 
                editorState={editorState}
                onChange={setEditorState}
            />
        </div>
    );
    
};
const TextBlock=({deleteButtonHandler,blockKey})=>{
常量[editorState,setEditorState]=useState(
()=>EditorState.createEmpty(),
);
常量工具栏ClickHander=(按钮类型,e)=>{
e、 预防默认值();
开关(按钮式){
案例“删除”:
//删除此处调用的按钮处理程序
deleteButtonHandler(区块键);
打破
违约:
打破
}
};
返回(
);
};

如果我通过
textButtonHandler
将一个元素添加到
,组件将按预期呈现在屏幕上。但是,如果我点击delete按钮并调用
deleteButtonHandler
,它会将数组大小记录为
0
,奇怪的是,如果我添加第二个元素,然后点击该元素的delete按钮,如果将数组大小记录为
1
。它几乎就像是在拍摄
状态的快照,就像新的
文本按钮Handler
被分配给道具,而不是使用
的实际当前状态一样。你知道我可能做错了什么吗?我以前没有遇到过这个问题。谢谢

好的。这里发生了什么:

在对象中传递函数。该对象可能有自己的上下文,并且您尝试在该对象上下文中使用该函数,这会使您感到困惑。(我知道ECMAScript simple对象没有自己的上下文,但react可能会处理该数据,因此可能以不同的方式工作。)
因此,将每个函数传递给子组件中的独立属性

例如:

import React,{useState}来自“React”;
导入“/styles.css”;
导出默认函数App(){
常量[块,立根块]=使用状态([
{key:Math.random(),deleteButtonHandler}
]);
const textButtonHandler=()=>{
const key=Math.random();
//常量数组=blocks.concat({
//钥匙,
//deleteButtonHandler:deleteButtonHandler
// });
setblock(prev=>prev.concat({key,deleteButtonHandler}));
};
const deleteButtonHandler=blockKey=>{
//测试功能,如果添加了一个文本字段,则arrray大小应为
//是1
控制台.日志(块.长度);
};
返回(
处理者
);
}
const NewPageContent=({blocks=[],deleteButtonHandler=()=>null})=>{
返回(
{blocks.map(block=>(
))}
);
};
const TextBlock=({deleteButtonHandler=()=>null,blockKey=”“})=>{
返回(
{blockKey}
X
);
};

我已经将你的旧解决方案进行了比较。

你能把它扔到沙箱中吗?请制作一个可生产的,看看你是不是在同一个函数中的
setBlocks
调用之后,就在控制台上记录
?好的,是的,我明白了。基本上每次
NewPage
呈现时,它都会重新创建
textButtonHandler
并作为回调附加。调用
textButtonHandler
时,它会包含
deleteButtonHandler
的一个“实例”,该实例还包含该渲染周期中
块的当前实例。调用
deleteButtonHandler
的实例时,它会记录从存储模块开始的
blocks.length
的值。因此,您会为每个条目传递一个delete方法。如果您编写了一个通用的delete函数,只需传入一个键、索引或id,并在根(状态存储的地方)处理该操作会怎么样?现在这就非常有意义了!谢谢我不知道为什么要在对象中传递函数。脑筋急转弯!谢谢在本例中,为什么使用concat而不是push?因为您应该始终返回一个新状态,而不是状态修改。这这可能会有所帮助:如果您返回一个经过修改的旧状态,react将不会重新启动该数组
const TextBlock = ({ deleteButtonHandler, blockKey }) => {
    const [editorState, setEditorState] = useState(
        () => EditorState.createEmpty(),
    );

    const toolbarClickHander = (buttonType, e) => {
        e.preventDefault();
        switch (buttonType) {
            case 'delete':
                // Delete button handler called here
                deleteButtonHandler(blockKey);
                break;
            default:
                break;
        }
    };

    return(
        <div className='text-block'>
            <TextBlockToolbar clickHandler={toolbarClickHander} />
             <Editor 
                editorState={editorState}
                onChange={setEditorState}
            />
        </div>
    );
    
};
import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [blocks, setBlocks] = useState([
    { key: Math.random(), deleteButtonHandler }
  ]);

  const textButtonHandler = () => {
    const key = Math.random();
    // const array = blocks.concat({
    // key,
    // deleteButtonHandler: deleteButtonHandler
    // });
    setBlocks(prev => prev.concat({ key, deleteButtonHandler }));
  };

  const deleteButtonHandler = blockKey => {
    // Test functionality, if one text field was added arrray size should
    // be 1
    console.log(blocks.length);
  };
  return (
    <div>
      <div className="d-flex">
        <NewPageContent
          deleteButtonHandler={deleteButtonHandler}
          blocks={blocks}
        />
      </div>
      <button onClick={textButtonHandler}>Handler</button>
    </div>
  );
}

const NewPageContent = ({ blocks = [], deleteButtonHandler = () => null }) => {
  return (
    <div className="new-page-content-container border mr-5 ml-5 p-3">
      {blocks.map(block => (
        <TextBlock
          key={block.key}
          blockKey={block.key}
          // deleteButtonHandler={block.deleteButtonHandler}
          deleteButtonHandler={deleteButtonHandler}
        />
      ))}
    </div>
  );
};

const TextBlock = ({ deleteButtonHandler = () => null, blockKey = "" }) => {
  return (
    <div className="text-block">
      {blockKey}
      <span onClick={deleteButtonHandler}>X</span>
    </div>
  );
};