Tiqa / redux-polyglot

Polyglot.js bindings for Redux
MIT License
58 stars 13 forks source link

React and Redux implementation issue #105

Closed nahueld closed 5 years ago

nahueld commented 5 years ago

I found a problem when trying to implement this combination and I couldn't find examples.

Imagine the following component (dispatched action during bootstrap looks like this store.dispatch(setLanguage('en', { some: { text: 'yolo' } }));)

import { getP } from 'redux-polyglot';

const MyComp = ({t}) => (<div>{t('some.text')}</div>)

function mapStateToProps(state) {
  const { polyglotReducer } = state;
  const t = getP(polyglotReducer).t;
  return {
    t,
  };
}

export default connect(mapStateToProps)(MyComp);

The following component renders <div>some.text</div> instead of <div>yolo</div>

I see all functions coming from getP (i.e: t and all the variations) return undefined.

Is this a bug?

guillaumearm commented 5 years ago

Hi,

Please re-check https://github.com/Tiqa/redux-polyglot#setup

I guess your reducer look like :

const rootReducer = combineReducers({
    ...yourReducers,
    polyglotReducer,
});

Correct usage is :

const rootReducer = combineReducers({
    ...yourReducers,
    polyglot: polyglotReducer,
});

The polyglot state must be in state.polyglot, so you have to change your mapStateToProps too :

  const { polyglot } = state;
  const t = getP(polyglot).t;
  return {
    t,
  };
}
nahuelmyob commented 5 years ago

Completely right, my mistake.

Thanks for the clarification.

guillaumearm commented 5 years ago

Glad I helped you 🙂

zedtux commented 5 years ago

I'm facing this issue too, my state does have the polyglot node, full of the translation keys fetched from the polyglot middleware :

const middlewares = []

// ...

/* redux-polyglot middleware
**
*  This middleware loads the translation keys from the given locale
*  so that dispatching the I_18_N_SWITCH_LANGUAGE action will load the right
*  translation keys.
*/
const polyglotMiddleware = createPolyglotMiddleware(
  'I_18_N_SWITCH_LANGUAGE',
  action => action.locale,
  locale => new Promise((resolve, reject) => {
    i18nService.fetch(locale).then(
      (translationKeys) => {
        resolve(translationKeys[locale])
      },
      (error) => {
        console.log('[Polyglot middleware] ERROR while fetching translation keys:', error)
        reject()
      }
    )
  })
)
middlewares.push(polyglotMiddleware)

// ...

export default function configureStore(initialState) {
  const store = createStore(rootReducer, initialState, compose(
    applyMiddleware(...middlewares),
    devToolsExtension
  ))
  return store
}

screenshot 2018-11-21 at 06 55 17

With this, I'm trying now to translate my buttons from my component :

import React from 'react'
// ...
import { getP } from 'redux-polyglot'

// ...

class TopBar extends React.Component {
  // ...

  render() {
    const { t } = this.props
    const { isOpen } = this.state

    return (
      <Navbar color="light" light expand="md">
        <NavbarToggler onClick={() => this.toggle()} />
        <Collapse isOpen={isOpen} navbar>
          <Nav className="ml-auto" navbar>
            <NavItem>
              <NavLink href="/users/sign_in">{t('shared.buttons.login')}</NavLink>
            </NavItem>
            <NavItem>
              <NavLink href="/users/sign_up">{t('shared.buttons.register')}</NavLink>
            </NavItem>
          </Nav>
        </Collapse>
      </Navbar>
    )
  }
}

function mapStateToProps(state) {
  const { polyglot } = state
  const { t } = getP(polyglot)
  return {
    home: state.home,
    t
  }
}

// ...

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TopBar)

But I see the shared.buttons... instead of the French translated labels.

Could you please help me ?

guillaumearm commented 5 years ago

Hi @zedtux

getP should be considered as a regular redux selector, so it takes directly the root state in parameter:

const { t } = getP(state);
zedtux commented 5 years ago

Thank you @guillaumearm for your reply.

Isn't what I did from the mapStateToProps function? It looks like a redundant thing in my eyes.

JalilArfaoui commented 5 years ago

@guillaumearm meant that your mapStateToProps should look like this:

function mapStateToProps(state) {
  const { t } = getP(state)
  return {
    home: state.home,
    t
  }
}

or even, in a more simple/ES6 style:

const mapStateToProps = state => ({
    home: state.home,
    t: getP(state).t
})
zedtux commented 5 years ago

Ah ! Thank you @JalilArfaoui for the clarification ! I'll try that @guillaumearm, thank you.

JalilArfaoui commented 5 years ago

I updated my previous message (thanks @guillaumearm for pointing out my mistake ;-) )

zedtux commented 5 years ago

Thank you @guillaumearm and @JalilArfaoui, it's now working.