我有一个函数组件,我想强制它重新渲染。

我该怎么做呢? 因为没有实例this,所以我不能调用this. forceupdate()。


当前回答

公认的答案是好的。 只是为了更容易理解。

示例组件:

export default function MyComponent(props) {

    const [updateView, setUpdateView] = useState(0);

    return (
        <>
            <span style={{ display: "none" }}>{updateView}</span>
        </>
    );
}

强制重新渲染调用下面的代码:

setUpdateView((updateView) => ++updateView);

其他回答

这可以在不显式使用钩子的情况下完成,只要你给你的组件添加一个道具,给无状态组件的父组件添加一个状态:

const ParentComponent = props => {
  const [updateNow, setUpdateNow] = useState(true)

  const updateFunc = () => {
    setUpdateNow(!updateNow)
  }

  const MyComponent = props => {
    return (<div> .... </div>)
  }

  const MyButtonComponent = props => {
    return (<div> <input type="button" onClick={props.updateFunc} />.... </div>)
  }

  return (
    <div> 
      <MyComponent updateMe={updateNow} />
      <MyButtonComponent updateFunc={updateFunc}/>
    </div>
  )
}

这些都没有给我一个满意的答案,所以最后我得到了我想要的关键道具,useRef和一些随机id生成器,如shortid。

基本上,我想要一些聊天应用在别人第一次打开应用时发挥自己的作用。所以,我需要完全控制什么时候和什么答案更新与异步等待的易用性。

示例代码:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// ... your JSX functional component, import shortid somewhere

const [render, rerender] = useState(shortid.generate())

const messageList = useRef([
    new Message({id: 1, message: "Hi, let's get started!"})
])

useEffect(()=>{
    async function _ () {
      await sleep(500)
      messageList.current.push(new Message({id: 1, message: "What's your name?"}))
      // ... more stuff
      // now trigger the update
      rerender(shortid.generate())
   } 
   _()
}, [])

// only the component with the right render key will update itself, the others will stay as is and won't rerender.
return <div key={render}>{messageList.current}</div> 

事实上,这也允许我滚动一些像聊天信息滚动。

const waitChat = async (ms) => {
    let text = "."
    for (let i = 0; i < ms; i += 200) {
        if (messageList.current[messageList.current.length - 1].id === 100) {
            messageList.current = messageList.current.filter(({id}) => id !== 100)
        }
        messageList.current.push(new Message({
            id: 100,
            message: text
        }))
        if (text.length === 3) {
            text = "."
        } else {
            text += "."
        }
        rerender(shortid.generate())
        await sleep(200)
    }
    if (messageList.current[messageList.current.length - 1].id === 100) {
        messageList.current = messageList.current.filter(({id}) => id !== 100)
    }
}

最佳方法-没有多余的变量重新创建在每次渲染:

const forceUpdateReducer = (i) => i + 1

export const useForceUpdate = () => {
  const [, forceUpdate] = useReducer(forceUpdateReducer, 0)
  return forceUpdate
}

用法:

const forceUpdate = useForceUpdate()

forceUpdate()

对我来说,仅仅更新状态是行不通的。我正在使用带有组件的库,看起来我不能强制组件更新。

我的方法是用条件渲染扩展上面的方法。在我的例子中,我想在值改变时调整组件的大小。

//hook to force updating the component on specific change
const useUpdateOnChange = (change: unknown): boolean => {
  const [update, setUpdate] = useState(false);

  useEffect(() => {
    setUpdate(!update);
  }, [change]);

  useEffect(() => {
    if (!update) setUpdate(true);
  }, [update]);

  return update;
};

const MyComponent = () => {
  const [myState, setMyState] = useState();
  const update = useUpdateOnChange(myState);

  ...

  return (
    <div>
      ... ...
      {update && <LibraryComponent />}
    </div>
  );
};

您需要传递想要跟踪更改的值。钩子返回用于条件呈现的布尔值。

当更改值触发时,useEffect更新将变为false,从而隐藏组件。在此之后,第二个useEffect被触发,update变为true,这使得组件再次可见,并导致更新(在我的例子中是调整大小)。

如果您正在使用版本< 16.8的功能组件。一种解决方法是直接调用相同的函数

import React from 'react';

function MyComponent() {
    const forceUpdate = MyComponent();
    
    return (
        <div>
            <button onClick={forceUpdate}>
                Click to re-render
            </button>
        </div>
    );
}

但如果你给它一些支撑,它就会断裂。在我的情况下,我只是通过相同的道具,我收到了渲染功能。