React Redux:更新 redux 状态依赖项时不触发 useEffect

问题描述 投票:0回答:1

我有一个非常简单的 Tile 组件,我想在悬停时更改颜色(以及其他关联的组件)。现在,当组件悬停时,它会设置 redux 状态。设置状态后,我想以不同的颜色重新渲染组件(和其他组件)。我正在使用 useEffect React 钩子,它依赖于我用 useSelector 声明的 redux 状态。

当这段代码运行时,我可以在 Redux Tools 浏览器插件中成功看到 redux 状态更新,但 useEffect 函数永远不会被触发。有人可以帮我理解为什么吗?

Tile 组件代码:

import React, { useEffect } from 'react';
import { useDispatch, useSelector, } from 'react-redux';
import { setSelectedTile } from './app/slices/tileSlice';
import Player from './Player';

const baseTileStyles = {
  backgroundColor: "lightgray",
  transition: "ease 500ms background-color",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  height: "100%",
  width: "100%"
};

const selectedStyles = {
  ...baseTileStyles,
  backgroundColor: "gray"
};

const getTileStyle = isSelected => {
  return isSelected ? selectedStyles : baseTileStyles;
};

export const Tile = (props) => {
  const dispatch = useDispatch()
  let tilestate = useSelector(state => state.selectedTile)

  useEffect(() => {
    console.log('selectedTile updated %d', tilestate)
  }, [tilestate])

  return (
    <div onMouseEnter={() => dispatch(setSelectedTile(props.id))} style={getTileStyle(tilestate === props.id)}>
      <Player streamUrl={props.streamUrl} isSelected={props.isSelected} />
    </div>
  )
}

export default Tile;

平铺状态减速器:

import { createSlice } from '@reduxjs/toolkit'

export const tileSlice = createSlice({
  name: 'tile',
  initialState: {
    selectedTile: 1,
    clickedTile: null
  },
  reducers: {
    setClickedTile: (state, action) => {
      if (state.clickedTile === action.payload) {
        return { ...state, clickedTile: null }
      } else {
        return { ...state, clickedTile: action.payload, selectedTile: action.payload }
      }
    },
    setSelectedTile: (state, action) => {
      if (!state.clickedTile) {
        return { ...state, selectedTile: action.payload }
      }
    }
  }
})

export const { setClickedTile, setSelectedTile } = tileSlice.actions

export default tileSlice.reducer

店铺:

import { configureStore } from '@reduxjs/toolkit'
import tileReducer from './slices/tileSlice'
import urlReducer from './slices/urlSlice'
import showHelpReducer from './slices/showHelpSlice'

export default configureStore({
    reducer: {
        window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
        tile: tileReducer,
        url: urlReducer,
        showHelp: showHelpReducer,
    }
})

我还尝试连接到 store.getState() 的回调,但这也总是返回 redux 状态的初始值,而不是更新后的状态。

reactjs redux react-hooks react-redux
1个回答
0
投票

selectedTile
状态值存储在
state.tile
状态中,而不是根
state
中。当您查看存储的创建方式以及用于形成状态树的减速器时,您会发现这一点是显而易见的。

export default configureStore({
  reducer: {
    tile: tileReducer,         // <-- state.tile
    url: urlReducer,           // <-- state.url
    showHelp: showHelpReducer, // <-- state.showHelp
  },
});

更新您的选择器以选择正确的状态:

来自

const tilestate = useSelector((state) => state.selectedTile);

const tilestate = useSelector((state) => state.tile.selectedTile);

我还建议使用更准确的变量名称,即

selectedTile

export const Tile = (props) => {
  const dispatch = useDispatch();
  const selectedTile = useSelector((state) => state.tile.selectedTile);

  useEffect(() => {
    console.log("selectedTile updated %d", selectedTile);
  }, [selectedTile]);

  return (
    <div
      onMouseEnter={() => dispatch(setSelectedTile(props.id))}
      style={getTileStyle(selectedTile === props.id)}
    >
      <Player streamUrl={props.streamUrl} isSelected={props.isSelected} />
    </div>
  );
};

我进一步建议使用 CSS 而不是内联

style
标签。

样式.css

.base-tile {
  background-color: lightgray;
  transition: ease 500ms background-color;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 100%;
}

.base-tile.selected {
  background-color: gray;
}
...
import "./styles.css";

export const Tile = (props) => {
  const dispatch = useDispatch();
  const selectedTile = useSelector((state) => state.tile.selectedTile);

  useEffect(() => {
    console.log("selectedTile updated %d", selectedTile);
  }, [selectedTile]);

  return (
    <div
      onMouseEnter={() => dispatch(setSelectedTile(props.id))}
      className={["base-tile", selectedTile === props.id && "selected"]
        .filter(Boolean)
        .join(" ")}
    >
      <Player streamUrl={props.streamUrl} isSelected={props.isSelected} />
    </div>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.