【问题标题】:How exactly does redux re-renders a react component?redux 究竟是如何重新渲染一个 react 组件的?
【发布时间】:2021-06-27 17:57:48
【问题描述】:

例如,我有一个初始状态,例如从 db 的 fetch post 请求中设置初始状态:

const initialState = {
  posts: [],
};

export default function (state = initialState, action) {
  const { type, payload } = action;

  switch (type) {
    case SET_APP_INITIAL_STATE:
      return {
        posts: payload.posts,
      };
   
...

注意:这里的帖子还包含需要在前端显示的数组缓冲区(有点重),即视频、图像等

然后在一个组件中,我使用 react redux 和 thunk reducers 获取这些并尝试显示它们,我执行以下操作:

const { posts: defaultPosts } = useSelector(state => state.myApp);


{defaultPosts && defaultPosts.length > 0 ? defaultPosts.map((singlePost, index) =>(
        <div key={index} className="post">
          <div className="postTop">
            <Avatar
              src={singlePost.profilePic}
              className="postTopAvatar"
            />
            <div className="postTopInfo">
              <h3>{singlePost.name}</h3>
            </div>
          </div>

        {singlePost.type === 'VIDEO' && singlePost.attachedMedia ? 
          <div className="postImage">
              <Video media={singlePost.attachedMedia} />
          </div>
        : null}

        {singlePost.type === 'PHOTO' && singlePost.attachedMedia ? 
          <div className="postImage">
              <Image media={singlePost.attachedMedia} />
          </div> 
        : null}
 .....

这里 Video/Image 组件利用 ref 并使用下面的 url 设置 src 以显示视频/图像等:

注意:singlePost.attachedMedia 包含数组缓冲区和 mime 类型信息

    const image = new Blob([arraybuffer], {
      type: mimeType
    });
    const url = URL.createObjectURL(image);

问题:

  1. 我不尝试深度克隆整个状态,只通过执行以下操作对其进行修改:
    case SET_APP_POST_LIKE:
      state.posts[payload.index].like = true;
      return {
        ...state
      };

这是错的吗?阅读其他博客文章似乎是一种非常糟糕的做法,但我不确定是否每次都进行深度克隆?

  1. 即使我按下一个赞按钮,我的整个帖子页面也会再次呈现?有没有办法不渲染任何东西,只渲染类似按钮。我已经通过播放视频进行了测试,当我按下它时它从开始开始并且还需要一秒钟才能加载。 (我认为它会重新渲染)

  2. 我还应该将我在帖子中包含所有内容的状态更改为如下所示。如果是,那么如果我尝试更改帖子数组会发生什么?整个状态会再次重新渲染吗?

const initialState = {
  posts: [],
  media: [],
  comments: []
};

请提供任何帮助,我们将不胜感激,在这里停留了一段时间。如果您需要任何澄清,请告诉我。谢谢!


编辑 1:


现在我只有 posts 数组,其中包含所有媒体(图像、视频的数组缓冲区)、cmets 和其他内容。你的意思是我应该把它们分成{帖子、媒体、cmets ...}吗?我试着做你提到的,即

const initialState = {
  posts: [],
  media: []
};

我现在将媒体称为 const { media } = useSelector(state => state.posts); 我将我的状态更新为

case SET_FB_POST_LIKE:
      state.posts[payload.index].like = true;
      return {
        ...state
      };

但还是没有运气!

【问题讨论】:

  • 这样看:defaultPosts?.length > 0 &&
    something..
    比较干净
  • 谢谢,好收获!

标签: reactjs redux react-redux


【解决方案1】:

指定分离的减速器,以便您可以使用特定的选择器,然后将您的减速器组合到全局存储中,如下所示:

const reducers = combineReducers({
  reducePosts,
  reduceMedia,
  reduceComments
});

const store = createStore(reducers, applyMiddleware(thunk));

export default store;

那么你的选择器应该是这样的单个钩子:

const posts = useSelector(state => state.reducePosts.posts);

【讨论】:

  • 我对原帖进行了修改,您可以参考一下吗!
【解决方案2】:

我不会尝试深度克隆整个状态,而只是通过执行以下操作来修改它:

case SET_APP_POST_LIKE:
 state.posts[payload.index].like = true;
 return {
   ...state
 };

这是错的吗?阅读其他博客文章似乎是一种非常糟糕的做法,但我不确定是否每次都进行深度克隆?

是的,这是一种非常糟糕的做法,但深度克隆也是一种非常糟糕的做法!

您所做的是通过在数组中的对象上分配 like 属性来直接改变状态。您需要返回一个新状态,其中除了posts 之外的所有属性都被复制。 posts 将是一个新数组,其中所有元素都是相同的对象,但您正在修改的元素除外,它是一个新对象。

这不是深度克隆,因为唯一被克隆的对象是实际更改的对象。

看起来像:

case SET_APP_POST_LIKE:
  return {
    ...state,
    posts: state.posts.map((post, i) =>
      i === payload.index
        ? {
            ...post,
            like: true
          }
        : post
    )
  };

真是让人头疼!


编写 Redux 的现代方式是使用 Redux Toolkit,这使得像这样更新深度嵌套的属性变得非常容易。您只需通过直接修改草稿来更新“草稿”状态,而不是返回新状态。

你的各种状态分支变成了“切片”,每个都有自己的减速器,但被组合成一个状态。

// create the reducer and actions for the posts slice
const postsSlice = createSlice({
  name: 'posts',
  initialState: [],
  reducers: {
    setAppPostLike(state, action) {
      // just modify the property, don't return anything
      state[action.payload].like = true;
    }
  }
})

// action creator functions
export const {setAppPostLike} = postsSlice.actions;

// combine slice reducers into a store
export const store = configureStore({
  reducer: {
    posts: postsSlice.reducer,
    media: // some reducer,
    comments: // some reducer,
  }
})

要喜欢第 3 条帖子,请致电 dispatch(setAppPostLike(3))


如果我尝试更改帖子数组会发生什么?整个状态会再次重新渲染吗?

state 对象是一个新对象,但mediacomments 属性相同。你的组件应该使用useSelector 来选择他们需要的状态部分。如果他们选择的部分没有改变,那么他们就不会重新渲染。

而不是选择整个myApp 属性

const { posts: defaultPosts } = useSelector(state => state.myApp);

只需选择帖子

const defaultPosts = useSelector(state => state.myApp.posts);

或特定的帖子

const post = useSelector(state => state.myApp.posts[index]);

【讨论】:

    猜你喜欢
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 2017-06-17
    • 2021-05-03
    • 2020-09-29
    • 2021-03-16
    相关资源
    最近更新 更多