2

The Blueprint UI library provides a Toaster component that displays a notice for a user's action. From the documentation, it's used by first calling

const MyToaster = Toaster.create({options}), followed by

MyToaster.show({message: 'some message'}).

I'm having trouble fitting the show method into React's lifecycle - how do I create a reusable toaster component that will display different messages on different button clicks? If it helps, I am using MobX as a data store.

approxiblue
  • 6,982
  • 16
  • 51
  • 59
plod
  • 77
  • 6
  • I have the same problem. I solved partially putting Mytoaster.show in the render method with a if control. I use Redux, and when my error props change I render Mytoaster.show, else null. Now my problem is fire an action on dismiss.. – Arcy Nov 28 '16 at 23:10

2 Answers2

1

The Toaster is a funny one in this regard because it doesn't care about your React lifecycle. It's meant to be used imperatively to fire off toasts immediately in response to events.

Simply call toaster.show() in the relevant event handler (whether it's a DOM click or a Redux action).

See how we do it in the example itself: toastExample.tsx

Gilad Gray
  • 451
  • 2
  • 6
0

My solution with Redux (and redux-actions)...

Action:

export const setToaster = createAction('SET_TOASTER');

Reducer:

const initialState = {
  toaster: {
    message: ''
  }
};

function reducer(state = initialState, action) {
  switch(action.type) {
    case 'SET_TOASTER':
      return {
        ...state, 
        toaster: { ...state.toaster, ...action.payload }
      };
  };
}

Toaster Component:

// not showing imports here...

class MyToaster extends Component {
  constructor(props) {
    super(props);

    this.state = {
      // set default toaster props here like intent, position, etc.
      // or pass them in as props from redux state
      message: '',
      show: false
    };
  }

  componentDidMount() {
    this.toaster = Toaster.create(this.state);
  }

  componentWillReceiveProps(nextProps) {
    // if we receive a new message prop 
    // and the toaster isn't visible then show it
    if (nextProps.message && !this.state.show) {
      this.setState({ show: true });
    }
  }

  componentDidUpdate() {
    if (this.state.show) {
      this.showToaster();
    }
  }

  resetToaster = () => {
    this.setState({ show: false });

    // call redux action to set message to empty
    this.props.setToaster({ message: '' });
  }

  showToaster = () => {
    const options = { ...this.state, ...this.props };

    if (this.toaster) {
      this.resetToaster();
      this.toaster.show(options);
    }
  }

  render() {
    // this component never renders anything
    return null;
  }
}

App Component:

Or whatever your root level component is...

const App = (props) =>
  <div>
    <MyToaster {...props.toaster} setToaster={props.actions.setToaster} />
  </div>

Some Other Component:

Where you need to invoke the toaster...

class MyOtherComponent extends Component {
  handleSomething = () => {
    // We need to show a toaster!
    this.props.actions.setToaster({
      message: 'Hello World!'
    });
  }

  render() { ... }
}
jdub
  • 160
  • 1
  • 8
  • I have some questions about this as I'm trying to implement this in quite a large code base. Any chance you have a few minutes? Can give you my email @logik – joshuaaron Apr 03 '17 at 08:46
  • @joshuaaron Sure thing, what's your question? – jdub Apr 04 '17 at 15:01
  • could you possibly shoot me an email at josh_reynolds_@hotmail.com, questions with screenshots will help. – joshuaaron Apr 05 '17 at 07:21