I need to display a list of objects in my Explorer component.
In my app I use useReducer
hook what wrapped by Context
.
It works well when the data flow is in "input-mode" (when I update data in state). But it does not rerender the application after data was changed.
So, steps that I need to pass and get a positive result.
- Press btn with file icon (or folder icon). This btn call hook that take a look into state and make a decision: where this file or folder should be placed in my simple fs.
- Write file/folder name (this function doesn't exist yet).
- Apply name by press enter or click mouse out of input (the same as 2 step).
Currently, I try to create file/folder with hardcoded name for testing 1-step. And I expect that dispatch
function pass data to the state and it would be updated and rerendered. All process runs well except of rerender.
I explain the flow of 1-st step.
- After I click btn, I call the func from hook for forming my instance.
- Then, the new instance saving into local
useState
. - After local
useState
successfully was updated, I calldispatch
inuseEffect
hook. - In reducer I modify my state and return it.
After this steps I expect that my app will automatically rerendered, but it isn't.
Code snippets, step by step.
- First step.
const handleFileClick = () => {
formAnInstance('file');
console.log('file btn click')
};
- Second step.
// in useInstancesInteraction hook
const { state, dispatch } = useStateContext();
const [instance, setInstance] = useState<IInstance>();
const formAnInstance = (mode: Omit<Mode, 'root'>) => {
if (
typeof state?.currentFolder === 'undefined' ||
state?.currentFolder === null
) {
const target =
mode === 'folder'
? (createInstance('folder', 'folder') as IInstance)
: (createInstance('file', 'file') as IInstance);
target['..'] = '/';
setInstance(target);
}
};
- Third step.
// in useInstancesInteraction hook
useEffect(() => {
const updateData = () => {
if (dispatch && instance) {
dispatch(createRootInstance(instance));
}
};
updateData();
}, [instance]);
- Fourth step.
export const initialState = {
root: createInstance('/', 'root') as IInstance,
currentFolder: null,
};
const reducer = (state = initialState, action: IAction) => {
const { type, payload } = action;
switch (type) {
case ACTION_TYPES.CREATE_ROOT_INSTANCE:
const myKey = payload['.'];
Object.assign(state.root, { [myKey]: payload });
console.log('Reducer', state?.root);
return state;
case ACTION_TYPES.CREATE_FILE:
break;
case ACTION_TYPES.UPLOAD_FILE:
break;
case ACTION_TYPES.RENAME_FILE:
break;
case ACTION_TYPES.DELETE_FILE:
break;
case ACTION_TYPES.CREATE_FOLDER:
break;
case ACTION_TYPES.RENAME_FOLDER:
break;
case ACTION_TYPES.DELETE_FOLDER:
break;
default:
return state;
}
};
Here how my context file look like:
import React, { useContext, useReducer } from 'react';
import { IContext } from './index.types';
import reducer, { initialState } from './reducer';
const StateContext = React.createContext<IContext>({
state: undefined,
dispatch: null,
});
const StateProvider = ({
children,
}: {
children: JSX.Element | JSX.Element[];
}) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<StateContext.Provider value={{ state, dispatch }}>
{children}
</StateContext.Provider>
);
};
export default StateProvider;
export const useStateContext = () => useContext(StateContext);