tyom / storybook-addons

Useful add-ons for Storybook
https://tyom.github.io/storybook-addons/
MIT License
23 stars 2 forks source link

Decorated component re-mounts on dispatch #27

Open kovcic opened 2 years ago

kovcic commented 2 years ago

Hello,

I'm trying to use the context addon for input-based components. For example:

import { useEffect } from 'react';
import { withReactContext } from 'storybook-react-context';

const InputWrapper = ({ value, children }) => (
  <div>
    <p>{value}</p>
    {children}
  </div>
);

export default {
  component: InputWrapper,
  title: 'InputWrapper',
};

export const Input = (args, { context: [ state, dispatch ] }) => {
  const handleChange = (e) => {
    const { target: { value } } = e;

    dispatch({ value });
  };

  useEffect(() => {
    console.info('mount');

    return () => {
      console.info('unmount');
    }
  }, [])

  return (
    <InputWrapper value={state.value}>
      <input type="text" value={state.value} onChange={handleChange} />
    </InputWrapper>
  );
};
Input.decorators = [withReactContext({
  initialState: {
    value: 'Hello',
  },
})];
Input.args = {};

I'm noticing that on every dispatch (input value change) story is re-mounted. Is there a way to avoid that, because on each keystroke input is re-mounted and focus is lost?

Thx

LongLiveCHIEF commented 2 years ago

This is a fundamental laws of react thing. Context is essentially a prop that you don't have to pass. That means it is subject to the same rules as props... and when you change a component's props, the component re-renders.

acha5066 commented 1 year ago

I think this is actually a bug. I was able to get the focus to stay on the input by swapping out this decorator with my own simple one. The below decorator is working for me:

const withMyContext = (args) => (Story) => {
  const [state, dispatch] = useReducer(filterReducer, args.initialState);
  return (
    <SearchContext.Provider value={[state, dispatch]}>
      {Story()}
    </SearchContext.Provider>
  );
};
decorators: [
    withMyContext({
      Context: SearchContext,
      initialState: {
        ...initialPredictiveState,
        settings: {
          search: {
            suggestions: {
              title: "Recommended search text",
            },
          },
        },
      },
    }),
  ],