2

我正在将基于类的组件中的生命周期方法替换为useEffect钩子,因为我正在使用功能组件。我不确定我是否正确使用了 useEffect ..

我们不能指向以前的状态或道具useEffect,我该如何处理这些useEffect

假设我们有名为 src 的道具,我想检查它src是否发生了变化,如果没有,就会有一些逻辑。

componentDidUpdate(prevProps) {
  const { src } = this.props; 

if(prevProps.src !== src) {
   this.cleanUp(); 
   this.init(); 
} else{
   // todo something 
}
}

我该如何处理这个使用useEffect

4

2 回答 2

3

在这种情况下,想想useEffect有一个“钩子”,它依赖于在发生变化时触发它的东西。

如果你useEffect什么都不给,它只会在加载时触发一次。这相当于componentDidMount

如果你给它多个对象或元素跟随它会根据这些元素的变化触发。

TL;博士:

const App = props => {
   useEffect(() => {
      // your code
   }, [props.yourPropertyToWatch]);
}

更新

根据@hmr的反馈,添加清理也是理想的,这相当于componentWillUnmount.

const App = props => {
   useEffect(() => {
      // your code

      // once done with the component
      return () => {
         // your unmount code
      }
   }, [props.yourPropertyToWatch]);
}
于 2020-06-30T10:04:10.870 回答
2

你可以像这样使用它:

function App(props) {
  const { src } = props;
  React.useEffect(() => {
    init();
    return () => cleanup();
  }, [src]);

如果 cleanup 和 init 不是静态函数并且需要访问本地状态或道具,那么我需要查看更多代码,并且可能必须在 init 和 cleanup 上使用 useCallback 以便您可以使它们依赖于效果以避免过时的关闭或无限渲染.

下面是一个使用 useCallback 的外部初始化函数示例:

//message channel has getAll, sendMessage and addListener
//  this is just an example and added to demonstrate hooks
const messageChannel = (() => {
  //function creating id to give each message unique id
  const id = ((id) => () => id++)(1);
  let listeners = [];
  const data = {};
  const addListener = (channel, callback) => {
    listeners.push({ channel, callback });
    //return a function that when called will remove
    //  listener
    return () =>
      (listeners = listeners.filter(
        ({ callback: fn }) => callback !== fn
      ));
  };
  const dispatch = (channel, newData) =>
    listeners
      .filter(({ channel: c }) => c === channel)
      .forEach(({ callback }) => callback(newData));
  const sendMessage = (channel, message) => {
    if (!data[channel]) {
      data[channel] = [];
    }
    const newMessage = { id: id(), message };
    data[channel] = [newMessage, ...data[channel]];
    dispatch(channel, newMessage);
  };
  const getAll = (channel) => data[channel] || [];
  return {
    addListener,
    sendMessage,
    getAll,
  };
})(); //IIFE creating message channel

function Messages({ channel }) {
  //local messages state that holds messages for this channel
  const [messages, setMessages] = React.useState([]);
  //using useCallback so init only changes when channel changes
  const init = React.useCallback(
    //set local messages to all messages of this channel
    () => setMessages(messageChannel.getAll(channel)),
    [channel]//re create init when channel changes
  );
  React.useEffect(() => {
    //call init to set all messages of current channel
    init();
    //add listener for new messages on current channel
    //  addListener returns a function that will remove
    //  the listener when called
    const cleanup = messageChannel.addListener(
      channel,
      //handler for new messages will add new message
      //  to local list of messages
      (newMessage) =>
        //do not mutate state, create a new messages array
        setMessages((messages) => [newMessage, ...messages])
    );
    //return the cleanup function, when channel changes
    //  react will call it and it will remove listener
    //  for previous channel
    return cleanup;
  }, [channel, init]);
  //both channel and init are a dependency but init
  //  only changes when channel changes, it will still
  //  function when removing init from the dependencies
  //  but the linter will complain
  return (
    <div>
      <h3>channel: {channel}</h3>
      <ul>
        {messages.map(({ message, id }) => (
          <li key={id}>{message}</li>
        ))}
      </ul>
    </div>
  );
}
function App() {
  const [channel, setChannel] = React.useState('A');
  const [message, setMessage] = React.useState('');
  const sendMessage = () =>
    messageChannel.sendMessage(channel, message);
  return (
    <div>
      <div>
        <label>
          Channel
          <select
            onChange={(e) => setChannel(e.target.value)}
          >
            <option value="A">A</option>
            <option value="B">B</option>
          </select>
        </label>
      </div>
      <div>
        <input
          type="text"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
        />
        <button onClick={sendMessage}>send message</button>
      </div>
      <Messages channel={channel} />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

于 2020-06-30T10:16:34.700 回答