Skip to content

Commit 88276fb

Browse files
committed
Reset on empty raw data
1 parent 8de2d71 commit 88276fb

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

docs/callback-handling.review.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
For learning purposes I refactored a component into it's own NPM package. So it becomes reusable and the idea of React is fulfilled. You can find the NPM package here: https://github.com/openscript/react-dsv-import
2+
3+
When I tried to use the component I ran into a problem:
4+
5+
Cannot update a component (`SomeComponent`) while rendering a different component (`DSVImport$1`). To locate the bad setState() call inside `DSVImport$1`, follow the stack trace as described in https://fb.me/setstate-in-render
6+
7+
The problem is that my `DSVImport` component, which I split into its own NPM package, uses a context and if the data within this context changes the `onChange` callback is triggered. If the user of the `DSVImport` component sets another state within the callback, the error stated above arises.
8+
9+
The good news is, that I could fix the problem, but I would like you to review my [fix](https://github.com/openscript/react-dsv-import/commit/5ddb0daad7984123b2e51d7d96471c365d1957ed).
10+
11+
Before I was calling the `onChange` callback during the context update in the middleware:
12+
13+
export const createSimpleParserMiddleware = <T>(onChange?: (value: T[]) => void) => {
14+
return (state: State<T>, action: Actions<T>) => {
15+
let newState = reducer<T>(state, action);
16+
17+
if (action.type === 'setRaw') {
18+
const delimiter = detectDelimiterFromValue(action.raw);
19+
const parsed = parseData<T>(action.raw, state.columns, delimiter);
20+
if (onChange) {
21+
onChange(parsed);
22+
}
23+
newState = reducer<T>(state, { type: 'setParsed', parsed });
24+
}
25+
26+
return newState;
27+
};
28+
};
29+
30+
I've removed all this stuff and created a context consumer component, which calls the `onChange` callback within an `effect` (`useEffect`):
31+
32+
33+
interface EventListenerProps<T> {
34+
onChange?: (value: T[]) => void;
35+
}
36+
37+
const EventListener = <T extends { [key: string]: string }>(props: EventListenerProps<T>) => {
38+
const [context] = useDSVImport<T>();
39+
40+
useEffect(() => {
41+
if (context.parsed && props.onChange) {
42+
props.onChange(context.parsed);
43+
}
44+
});
45+
46+
return null;
47+
};
48+
49+
export interface Props<T> {
50+
onChange?: (value: T[]) => void;
51+
columns: ColumnsType<T>;
52+
}
53+
54+
export const DSVImport = <T extends { [key: string]: string }>(props: PropsWithChildren<Props<T>>) => {
55+
const DSVImportContext = getDSVImportContext<T>();
56+
const middleware = createSimpleParserMiddleware<T>();
57+
const initialValues: State<T> = { columns: props.columns };
58+
59+
return (
60+
<DSVImportContext.Provider value={useReducer(middleware, initialValues)}>
61+
<EventListener<T> onChange={props.onChange} />
62+
{props.children}
63+
</DSVImportContext.Provider>
64+
);
65+
};
66+
67+
68+
What do you think about this?

0 commit comments

Comments
 (0)