React fundamental
This post is a list of basic experiments, just to examine the React cache and DOM re-rendering fundamentals.
Let’s have the most simple React App. The purpose is to feel how changing one value will re-compute / re-rendering the app. Instead of checking the React-Dev-Tools, I added some logs.
const App = ({data}) => { console.log('App'); return <div> <TheDis d={data.d1} /> <TheDis2 d={data.d2} /> </div> } const TheDis = ({ d }) => { console.log(`TheDis: ${d.title}`); return <p>{d.title}</p> } const TheDis2 = ({d}) => { console.log(`TheDis2: ${d.title}`); return <p>{d.title}</p> } let data = { d1 : {title:'d1'}, d2 : {title:'d2'} }; ReactDOM.render(<App data={ data} />, document.querySelector('#app'));
And direct DOM manipulation to be able to see when React re-renders the DOM:
setTimeout(() => { document.querySelectorAll('p').forEach((e,i) => { e.innerText = `P number ${i}` ; }); console.log('Dom manipulation') }, 1000)
Changes outside React:
Changing the data outside the React will not trigger any action:
ReactDOM.render(<App data={ data} />, document.querySelector('#app')); setTimeout(() => { data = {...data, d1: { title: 'Nothing here' }}; }, 2000);
The console is empty, and the DOM didn’t change
Direct data change
Once more, changing the props has no meaning. And the console is empty.
const App = ({data}) => { console.log('App'); setTimeout(() => { data = {...data}; }, 2000); return <div> <TheDis d={data.d1} /> <TheDis2 d={data.d2} /> </div> }
Changing a state value
Changing a state value will re-render all the React tree, even id the state data is not used. But the DOM will not be effected.
const App = ({data}) => { console.log('App'); const [stateData,setStateData] = React.useState(data); setTimeout(() => { setStateData({...data}); }, 2000); return <div> <TheDis d={data.d1} /> <TheDis2 d={data.d2} /> </div> }
Note that the timeout re-renders couses and endless loop. So there is a need of useEffect
const App = ({data}) => { console.log('App'); const [stateData,setStateData] = React.useState(data); React.useEffect(()=>{ setTimeout(() => { setStateData({...data}); }, 2000); },[]); return <div> <TheDis d={stateData.d1} /> <TheDis2 d={stateData.d2} /> </div> }
The DOM will not change, but the log will show the re-computes:
App TheDis: d1 TheDis2: d2 Dom manipulation App TheDis: d1 TheDis2: d2
Adding React Memo
React memo caches the component according to the props, and will prevent the re-compute if the prop didn’t change.
const TheDis = React.memo(({ d }) => { console.log(`TheDis: ${d.title}`); return <p>{d.title}</p> }) const TheDis2 = React.memo(({d}) => { console.log(`TheDis2: ${d.title}`); return <p>{d.title}</p> })
Now the DOM still will not change, and the unchanged components will not re-render. It can be seen as the console is:
App TheDis: d1 TheDis2: d2 Dom manipulation App
Changing the data
Now, if the data will actually change…
const App = ({data}) => { console.log('App'); const [stateData,setStateData] = React.useState(data); React.useEffect(()=>{ setTimeout(() => { data.d1 = {title: 'some new title'} setStateData({...data}); }, 2000); },[]) return <div> <TheDis d={stateData.d1} /> <TheDis2 d={stateData.d2} /> </div> }
The sub-component will re-render, and the DOM will update:
App TheDis: d1 TheDis2: d2 Dom manipulation App TheDis: some new title
Back to external changes…
Skadoosh, success. But the goal was to change the data outside React…
…with Redux
Do I need to introduce react-redux? Before diving, and for the whoever that will read this. The code can be nicer, here I focused on the shortness.
const initialState = { d1 : {title:'d1'}, d2 : {title:'d2'} }; function rootReducer(state = initialState, action) { if (action.type === "CHANGE_DATA") { return {...state, d1: { ...action.payload }}; } return state; }; const store = createStore(rootReducer);
The changes in the React App
const mapStateToProps = (state) => { return {data:{...state}}; }; const AppRedux = connect(mapStateToProps)(App); ReactDOM.render(<Provider store={store}><AppRedux /></Provider>, document.querySelector('#app'))
And back to the Redux external changes
setTimeout(() => { store.dispatch({ type: "CHANGE_DATA", payload: {title:"Hi from Redux"} }); }, 2000)
…with mobX
import { makeAutoObservable } from "mobx" import { observer } from "mobx-react" class Data{ d1 = {title:'d1'}; d2 = {title:'d2'}; constructor(){ makeAutoObservable(this); } } const App = observer(({data}) => { console.log('App'); return <div> <TheDis d={data.d1} /> <TheDis2 d={data.d2} /> </div> }); let data = new Data(); ReactDOM.render(<App data={ data} />, document.querySelector('#app')) setTimeout(() => { data.d1 = { title: 'Hi from mobX' } }, 2000)
Conclusion
With both: mobX & Redux, React will be re-rendered even that the changes are executed outside the React. And the app will manipulate the DOM only for the relevant changed value. The console will display:
App TheDis: d1 TheDis2: d2 Dom manipulation App theDis: TheDis Hi from [mobX|Redux]
There is nothing like a theoretical understanding, a thesis that materializes on an actional app.
About the differences between Redux vs mobX, on some other time.