Reputation: 446
I'm trying to reselect
middleware today and prevent unnecessary re-rendering.
Here is my reducer.js:
const INITIAL_STATE = {
dogs: 100,
cats: 12312302384
};
const pets = (state = INITIAL_STATE, action) => {
switch (action.type) {
case "CHANGE_DOGS":
return {
...state, dogs: state.dogs + 1
};
case "CHANGE_CATS":
return {
...state, cats: state.cats + 1
};
default:
return { ...state };
}
};
export default pets;
Here is my main.js:
import React from "react";
import { createSelector } from "reselect";
import { useSelector, useDispatch } from "react-redux";
function ReduxDeneme(props) {
// Selectors - Classic style - NON Memoized!
// const dogsData = useSelector(state => state.pets.dogs);
// const catsData = useSelector(state => state.pets.cats);
// Dogs rendering.. --> First opening..
// 10:11:28.070 index.js:18 CATS rendering.. --> First opening..
// 10:11:29.703 index.js:13 Dogs rendering.. --> Press "ChangeDogs" button.
// 10:11:29.703 index.js:18 CATS rendering.. --> Press "ChangeDogs" button.
// 10:11:33.143 index.js:13 Dogs rendering.. --> Press "ChangeCats" button.
// 10:11:33.143 index.js:18 CATS rendering.. --> Press "ChangeCats" button.
// Selectors - Memoized version RESELECT middleware'i ile..
const dogsDataMemo = createSelector(
state => state.pets.dogs,
dogs => dogs
);
const catsDataMemo = createSelector(
state => state.pets.cats,
cats => cats
);
const dogsData = useSelector(dogsDataMemo)
const catsData = useSelector(catsDataMemo)
// Components
const Dogs = ({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
};
const Cats = ({ catsData }) => {
console.log("Cats rendering..");
return <h1>{catsData}</h1>;
};
// Actions
const dispatch = useDispatch();
const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
const changeCats = () => dispatch({ type: "CHANGE_CATS" });
return (
<div>
<Dogs dogsData={dogsData} />
<Cats catsData={catsData} />
<button onClick={changeDogs}>Change Dogs</button>
<button onClick={changeCats}>Change CATS</button>
</div>
);
}
export default ReduxDeneme;
reselect
middleware to prevent unnecessary re-renderings. But, It doesn't work or I have misunderstood meaning of reselect
.Could someone explain the correct way or where am I doing wrong?
Upvotes: 1
Views: 1680
Reputation: 446
First of all, I thank @tudor very much for his efforts. All of his said is correct.
But, I want to show that Reselect works.
SCENARIO 1 - NON-MEMOIZED
import React, { memo } from "react";
import { useSelector, useDispatch } from "react-redux";
// import { catsDataMemo, dogsDataMemo } from "./selectors";
// Components
const Dogs = memo(({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
});
const Cats = memo(({ catsData }) => {
console.log("Cats rendering..");
return <h1>{catsData}</h1>;
});
function ReduxDeneme() {
// Standart useSelector without MEMOIZED
const dogsData = useSelector(
state => state.pets.dogs,
console.log("dogsData Selector çalıştı.")
);
const catsData = useSelector(
state => state.pets.cats,
console.log("catsData Selector çalıştı.")
);
// Actions
const dispatch = useDispatch();
const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
const changeCats = () => dispatch({ type: "CHANGE_CATS" });
const noChangeCats = () =>
dispatch({ type: "NO_CHANGE_CATS", payload: catsData });
return (
<div>
<Dogs dogsData={dogsData} />
<Cats catsData={catsData} />
<button onClick={changeDogs}>Change Dogs</button>
<button onClick={changeCats}>Change CATS</button>
<button onClick={noChangeCats}>No Change</button>
</div>
);
}
export default memo(ReduxDeneme);
Be Careful! When you click the "Change Dogs" button, output in the console will be:
dogsData Selector çalıştı.
catsData Selector çalıştı.
Dogs rendering..
or when you click the "Change Cats" button, the output will be:
dogsData Selector çalıştı.
catsData Selector çalıştı.
Cats rendering..
Whatever button you press, both of useSelectors will work as you can see from console.log
SCENARIO 2 - MEMOIZED WITH RESELECT MIDDLEWARE
Firstly, we separate memoized selectors to another file as @tudor.gergely mentioned.
BE CAREFULL! You must define the correct path of an object.
// selectors.js
import { createSelector } from "reselect";
export const dogsDataMemo = createSelector(
state => state.pets.dogs, // BE CAREFULL while defining..
dogs => {
console.log("DogsDataMemo has worked.");
return dogs;
}
);
export const catsDataMemo = createSelector(
state => state.pets.cats, // BE CAREFULL while defining..
cats => {
console.log("CatsDataMemo has worked.");
return cats;
}
);
Then, we import this file into main.js file and use again useSelector with our memoized selectors:
import React, { memo } from "react";
import { useSelector, useDispatch } from "react-redux";
import { catsDataMemo, dogsDataMemo } from "./selectors";
// Components
const Dogs = memo(({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
});
const Cats = memo(({ catsData }) => {
console.log("Cats rendering..");
return <h1>{catsData}</h1>;
});
function ReduxDeneme() {
const dogsData = useSelector(dogsDataMemo);
const catsData = useSelector(catsDataMemo);
// Actions
const dispatch = useDispatch();
const changeDogs = () => dispatch({ type: "CHANGE_DOGS" });
const changeCats = () => dispatch({ type: "CHANGE_CATS" });
const noChangeCats = () =>
dispatch({ type: "NO_CHANGE_CATS", payload: catsData });
return (
<div>
<Dogs dogsData={dogsData} />
<Cats catsData={catsData} />
<button onClick={changeDogs}>Change Dogs</button>
<button onClick={changeCats}>Change CATS</button>
<button onClick={noChangeCats}>No Change</button>
</div>
);
}
export default memo(ReduxDeneme);
and the final output:
DogsDataMemo has worked.
Dogs rendering
CatsDataMemo has worked.
Cats rendering..
Upvotes: 1
Reputation: 4988
You are defining the selectors inside your component. You should do it outside (e.g. somewhere near your reducer).
Currently you are recreating the selector after each render. Here's a better way:
// inside reducer.js
export const petsSel = state => state.pets;
export const dogsDataMemo = createSelector(
petsSel,
pets => pets.dogs
);
export const catsDataMemo = createSelector(
petsSel,
pets => pets.cats
);
Added a codesandbox with a working example based on your code: https://codesandbox.io/s/delicate-snowflake-5ssrw
To achieve what you desire you need to use React.memo (https://reactjs.org/docs/react-api.html#reactmemo) as well:
const Dogs = React.memo(({ dogsData }) => {
console.log("Dogs rendering..");
return <h1>{dogsData}</h1>;
});
Upvotes: 1