FormidableLabs / freactal

Clean and robust state management for React and React-like libs.
MIT License
1.65k stars 46 forks source link

Add documentation to use with socket.io #52

Closed JustFly1984 closed 7 years ago

JustFly1984 commented 7 years ago

I did not understand from examples, how can I change the state of stateless component with socket.io

Currently my component's state heavily depends on data arrived from socket.io.

I see that you solved fetch-like pull requests, but how can I set state on push-like socket messages?

Right now I have stateful HOC with lifecycle.

I'm making socket initialization on componentDidMount, saving socket to redux store if componentWillUnmount to reuse socket connection on repeated mount. It feels like I need to keep my HOC components stateful, but how can I inject Freactal state from HOC component's lifecycle?

BTW for sure your lib is straight to the point replacement to redux. Thank you very much for your creative mindset!

divmain commented 7 years ago

This should be doable by creating the connection on componentDidMount of an injectedState-wrapped component.

class MyComponent extends Component {
  componentDidMount () {
    const { effects } = this.props;
    io().on("connection", socket => {
      // You can have 
      socket.on("server-message", effects.doSomethingWithServerMessage);
    });
  }
}

export default injectState(MyComponent);

In fact, now that I think about it, you could implement a wrapper function that maps socket IO events to effects. That would look something like this:

const mapSocketEventsToEffects = eventsToEffects => BaseComponent => injectState(
  class EventWrappedComponent extends Component {
    componentDidMount () {
      io().on("connection", socket => {
        this.socket = socket;
        Object.keys(eventsToEffects).forEach(eventName => {
          const effectName = eventsToEffects[eventName];
          socket.on(eventName, this.props.effects[effectName]);
        });
      });
    }

    render () {
      return <BaseComponent socket={this.socket} {...this.props} />
    }
  }
);

const wrapComponentWithState = provideState({
  initialState: () => ({ log: [] }),
  effects: {
    updateLog: (effects, logMessage) => state =>
      ({ ...state, log: state.log.concat(logMessage) })
  }
});

const App = ({ state }) => (
  <div>
    {state.log.map(logMessage => <span>{logMessage}</span>)}
  </div>
);

const withEvents = mapSocketEventsToEffects({
  "server-log-message": "updateLog"
});

const WrappedApp = wrapComponentWithState(withEvents(App));

ReactDOM.render(<WrappedApp />, document.getElementById("root"));

I hope that helps!

divmain commented 7 years ago

P.S. This is Socket.IO specific, so I won't be adding this to the official docs. Hopefully if someone wants the same thing, they'll find the issue instead!

JustFly1984 commented 7 years ago

Thank you! This explains a lot) But still can't use it in my current project without immutable.js root state object(