How to create new object as default arg object - javascript

So we may have this in a reducer:
const defaultState = {...};
export const userReducer = (state = defaultState, action: any) => {
// ...
}
is there some way to get a defaultState object for each call to userReducer?
Something like this:
const getDefaultState = () => ({...});
export const userReducer = (state = getDefaultState(), action: any) => {
// ...
}
is this possible in JS? It might not be useful for a Redux reducer, but in general am curious.

Yes, as #blex, pointed out, your intentions are completely doable.
Your snippet has a minor typo that may be causing you issues: parameters with default values (i.e. state) must be ordered after parameters with non-default values (i.e. action).
Here's a minimalist example:
let x = () => 3;
let y = (a, b = x()) => a + b;
console.log(y(5)); // 8
console.log(y(5, 1)); // 6

Related

React useState hook with dependency

TL;DR
Why there is no dependency array for useState(), something like:
const [state, setState] = useState<T>(initialState, [initialState]);
Question
In React I often end up in this situation
export function EditComponent<T>(props: {
initialState: T,
onSave: (value: T) => void,
}) {
const { initialState, onSave } = props;
const [state, setState] = useState<T>(initialState);
useEffect(() => setState(initialState), [initialState]);
function revert() {
setState(initialState);
}
function save() {
onSave(state);
}
return (
...
)
}
Where I have an outer component that provides some data and an inner component that lets the user edit it (by modifying a copy of the data) to then eventually save or revert. Of course, I need the inner component to be reactive to any outer change (maybe new data has been fetched from the network or whatever).
What I dislike is doing this:
const [state, setState] = useState<T>(initialState);
useEffect(() => setState(initialState), [initialState]);
Cause from my understanding of React this is rendering the component twice when initialState changes:
First to render its parent's children (due to initialState change). In this cycle, the useEffect update is added to the queue to be performed after rendering.
and then a second type after the useEffect update has been performed.
What I need is a dependency array on useState, to do:
const [state, setState] = useState<T>(initialState, [initialState]);
It seems something straightforward, the same as initialState is synchronously consumed, and made available, during the first rendering cycle, when the dependency list changes this operation shall be performed again.
I attempted to implement it myself, but what I came up with seems more like a hack:
import { DependencyList, Dispatch, SetStateAction, useRef, useState } from 'react';
interface MemoContext<S> {
deps: DependencyList | undefined;
state?: S
}
// Is dependency list equal (L327 areHookInputsEqual)
function areHookInputsEqual(a: DependencyList | undefined, b: DependencyList | undefined): boolean {
if (!a) {
console.error('Prev deps should not be null')
return false;
} else if (!b) {
return false;
}
for (let i = 0; i < a.length && i < b.length; i++) {
if (!Object.is(a[i], b[i])) {
return false;
}
}
return true;
}
export function useMemoState<S>(
initialState: S | (() => S),
deps?: DependencyList,
): [S, Dispatch<SetStateAction<S>>] {
function resetInitialState() {
const s: S = typeof initialState === 'function' ? (initialState as any)() : initialState;
ctx.state = s;
ctx.deps = deps;
return s;
}
const ctx = useRef<MemoContext<S>>({ deps: undefined, state: undefined }).current;
// this is actually used just to preserve the rendering behaviour
const [state, setState] = useState<S>(resetInitialState);
if (!areHookInputsEqual(ctx.deps, deps)) {
// They are different, perform the update
resetInitialState()
}
function dispatch(action: SetStateAction<S>) {
setState(prevState => {
const s: S = typeof action === 'function' ? (action as any)(prevState) : action;
ctx.state = s;
ctx.deps = deps;
return s;
})
}
return [ctx.state!, dispatch];
}
To be honest React Core seems like something that should not be touched. So I'm wondering if I'm missing something and if there is a clear reason why such a feature does not exist. Or maybe there is a better solution to this?
A short answer - we do not need it=)
This is your refactored code snippet:
export function EditComponent<T>(props: {
initialState: T,
onSave: (value: T) => void,
}) {
const { initialState, onSave } = props;
const [state, setState] = useState<T>(initialState);
if (initialState !== state){
setState(initialState);
}
function revert() {
setState(initialState);
}
function save() {
onSave(state);
}
return (
...
)
}
Just conditionally update state during rendering.
There are 2 links which you may find helpful:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops

Are there any drawbacks to eliminating the defaultstore?

I am refactoring my code to eliminate the default store and use ES6+ defaults as follows:
That is I am moving defaults from an object passed in as follows:
const store = createStore(reducers, defaultstore, middleware);
to directly into the reducers. This way I can locate them easier. The two methods are shown below ...
defaultstore
Menu: {
current: 'Articles'
},
reducer function
// People is the default
const Menu = (state = {current: 'People'}, action) => {
const newState = { ...state };
switch(action.type) {
case 'updateMenu':
newState.current = action.current;
return newState;
}
return state;
};
Is there any reason not to do this. It makes the code more readable.

React: break dependency between 2 related contexts with top-level constant object

I'm working on a new major release for react-xarrows, and I came up with some messy situation.
It's not going to be simple to explain, so let's start with visualization:
consider the next example - 2 draggable boxes with an arrow drawn between them, and a wrapping context around them.
focused code:
<Xwrapper>
<DraggableBox box={box} />
<DraggableBox box={box2} />
<Xarrow start={'box1'} end={'box2'} {...xarrowProps} />
</Xwrapper>
Xwrapper is the context, DraggableBox and Xarrow are, well, you can guess.
My goal
I want to trigger a render on the arrow, and solely on the arrow, whenever one of the connected boxes renders.
My approach
I want to be able to rerender the arrow from the boxes, so I have to consume 'rerender arrow'(let's call it updateXarrow) function on the boxes, we can use a context and a useContext hook on the boxes to get this function.
I will call XelemContext to the boxes context.
also, I need to consume useContext on Xarrow because I want to cause a render on the arrow whenever I decide.
this must be 2 different contexts(so I could render xarrow solely). one on the boxes to consume 'updateXarrow', and a different context consumed on Xarrow to trigger the reredner.
so how can I pass this function from one context to another? well, I can't without making an infinite loop(or maybe I can but could not figure it out), so I used a local top-level object called updateRef.
// define a global object
const updateRef = { func: null };
const XarrowProvider = ({ children }) => {
// define updateXarrow here
...
// assign to updateRef.func
updateRef.func = updateXarrow;
return <XarrowContext.Provider value={updateXarrow}>{children}</XarrowContext.Provider>;
};
//now updateRef.func is defined because XelemProvider defined later
const XelemProvider = ({ children }) => {
return <XelemContext.Provider value={updateRef.func}>{children}</XelemContext.Provider>;
};
the thing is, that this object is not managed by react, and also, i will need to handle cases where there is multiple instances of Xwrapper, and I'm leaving the realm of React, so i have 2 main questions:
there is a better approach? maybe I can someone achieve my goal without going crazy?
if there is no better option, is this dangerous? I don't want to release a code that will break on edge cases on my lib consumer's apps.
Code
DraggableBox
const DraggableBox = ({ box }) => {
console.log('DraggableBox render', box.id);
const handleDrag = () => {
console.log('onDrag');
updateXarrow();
};
const updateXarrow = useXarrow();
return (
<Draggable onDrag={handleDrag} onStop={handleDrag}>
<div id={box.id} style={{ ...boxStyle, position: 'absolute', left: box.x, top: box.y }}>
{box.id}
</div>
</Draggable>
);
};
useXarrow
import React, { useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { XelemContext } from './Xwrapper';
const useXarrow = () => {
const [, setRender] = useState({});
const reRender = () => setRender({});
const updateXarrow = useContext(XelemContext);
useLayoutEffect(() => {
updateXarrow();
});
return reRender;
};
export default useXarrow;
Xwrapper
import React, { useState } from 'react';
export const XelemContext = React.createContext(null as () => void);
export const XarrowContext = React.createContext(null as () => void);
const updateRef = { func: null };
const XarrowProvider = ({ children }) => {
console.log('XarrowProvider');
const [, setRender] = useState({});
const updateXarrow = () => setRender({});
updateRef.func = updateXarrow;
return <XarrowContext.Provider value={updateXarrow}>{children}</XarrowContext.Provider>;
};
const XelemProvider = ({ children }) => {
console.log('XelemProvider');
return <XelemContext.Provider value={updateRef.func}>{children}</XelemContext.Provider>;
};
const Xwrapper = ({ children }) => {
console.log('Xwrapper');
return (
<XarrowProvider>
<XelemProvider>{children}</XelemProvider>
</XarrowProvider>
);
};
export default Xwrapper;
const Xarrow: React.FC<xarrowPropsType> = (props: xarrowPropsType) => {
useContext(XarrowContext);
const svgRef = useRef(null);
....(more 1100 lines of code)
logs
I left some logs.
on drag event of a single box you will get:
onDrag
DraggableBox render box2
XarrowProvider
xarrow
Note
currently, this is working as expected.
Update
after many hours of testing, this seems to work perfectly fine. I manage my own object that remember the update function for each Xwrapper instance, and this breaks the dependency between the 2 contexts. I will leave this post in case someone else will also come across this issue.
Update (bad one)
this architecture breaks on react-trees with <React.StrictMode>...</React.StrictMode> :cry:
any idea why? any other ideas ?
just in case someone would need something similar: here's a version that will work even with react strictmode(basically being rellyed of effect which called once and not renders):
import React, { FC, useEffect, useRef, useState } from 'react';
export const XelemContext = React.createContext(null as () => void);
export const XarrowContext = React.createContext(null as () => void);
// will hold a object of ids:references to updateXarrow functions of different Xwrapper instances over time
const updateRef = {};
let updateRefCount = 0;
const XarrowProvider: FC<{ instanceCount: React.MutableRefObject<number> }> = ({ children, instanceCount }) => {
const [, setRender] = useState({});
const updateXarrow = () => setRender({});
useEffect(() => {
instanceCount.current = updateRefCount; // so this instance would know what is id
updateRef[instanceCount.current] = updateXarrow;
}, []);
// log('XarrowProvider', updateRefCount);
return <XarrowContext.Provider value={updateXarrow}>{children}</XarrowContext.Provider>;
};
// renders only once and should always provide the right update function
const XelemProvider = ({ children, instanceCount }) => {
return <XelemContext.Provider value={updateRef[instanceCount.current]}>{children}</XelemContext.Provider>;
};
const Xwrapper = ({ children }) => {
console.log('wrapper here!');
const instanceCount = useRef(updateRefCount);
const [, setRender] = useState({});
useEffect(() => {
updateRefCount++;
setRender({});
return () => {
delete updateRef[instanceCount.current];
};
}, []);
return (
<XelemProvider instanceCount={instanceCount}>
<XarrowProvider instanceCount={instanceCount}>{children}</XarrowProvider>
</XelemProvider>
);
};
export default Xwrapper;

Redux enhancer example

I am new to redux. I would like know how I could create my own enhancer in redux. I didn't find any example to create enhancer. To create enhancers, So what arguments do I need to pass and what do I need to return? Is there any rule on creating custom enhancer?
In redux documentation about enhancer, found below two links (no sample or example code)
store-enhancer
using store enhancer
Redux documentation said that,
Middleware adds extra functionality to the Redux dispatch function; enhancers add extra functionality to the Redux store. ... A middleware which logs dispatched actions and the resulting new state. An enhancer which logs the time taken for the reducers to process each action.
So, I am not sure that custom middleware and custom enhancer coding rule are the same like below
const loggerMiddleware = storeAPI => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', storeAPI.getState())
return result
}
So, my question is how to create custom enhancer?
Here is the store enhancer interface
export type StoreEnhancer<Ext = {}, StateExt = never> = (
next: StoreEnhancerStoreCreator<Ext, StateExt>
) => StoreEnhancerStoreCreator<Ext, StateExt>
export type StoreEnhancerStoreCreator<Ext = {}, StateExt = never> = <
S = any,
A extends Action = AnyAction
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>
) => Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
enhancers are high-order functions that take createStore and return a new enhanced version of createStore. Take a look at this sample implementation.
const ourAwesomeEnhancer = createStore => (reducer, initialState, enhancer) => {
const store = createStore(monitoredReducer, initialState, enhancer);
// add enhancer logic
return {
...store
// you can override the some store properties or add new ones
};
};
There is an example in official doc:
const round = number => Math.round(number * 100) / 100
const monitorReducerEnhancer = createStore => (
reducer,
initialState,
enhancer
) => {
const monitoredReducer = (state, action) => {
const start = performance.now()
const newState = reducer(state, action)
const end = performance.now()
const diff = round(end - start)
console.log('reducer process time:', diff)
return newState
}
return createStore(monitoredReducer, initialState, enhancer)
}
export default monitorReducerEnhancer

Structuring the store in Redux

Is there a way to structure const reducer = (state = initialState, action) in such a manner that the method isn't bloated by a bunch of switch cases?
My idea was to put related actions in arrays and check them with Array.prototype.includes() when handling an action.
I would then extract the switch cases that correlate to specific actions in new methods (for example the List component would have LIST_ADD, LIST_REMOVE etc.) and call those methods instead of just running through 100 cases in the const reducer = (state = initialState, action)method.
That would tax performance but it would be at least structured.
Any better ideas?
The offical Redux docs provide this very handy reducer creator:
function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}
which lets you create your reducer as follows:
const reducer = createReducer(initialState, {
[actionType.ACTION1]: specificActionReducer1,
[actionType.ACTION2]: specificActionReducer2,
}
No switch statements!
I use a library called reduxsauce which removes the need for large switch statements.
https://github.com/infinitered/reduxsauce
Instead it binds actions to methods with this syntax:
export const INITIAL_STATE = {
values: {},
}
export const reducerFunction = (state, action) => {
const values = action.value;
return {
...state,
values,
};
};
// map the action types to the reducer functions
export const HANDLERS = {
[Type.ACTION_NAME]: reducerFunction,
...
}
// call createReducer to magically tie it all together
export default createReducer(INITIAL_STATE, HANDLERS);
You could try redux-named-reducers for this as well. Allows you to compose reducers like so:
moduleA.reduce(SOME_ACTION, action => ({ state1: action.payload }))
moduleA.reduce(SOME_OTHER_ACTION, { state2: "constant" })
It has the added benefit of being able to access the reducer state anywhere, like in mapDispatchToProps for example:
const mapDispatchToProps = dispatch => {
return {
onClick: () => {
dispatch(someAction(getState(moduleA.state1)));
}
};
};

Categories