How to use Ant Design LocaleProvider with React-Boilerplate - reactjs

We have started using this React-Boilerplate . Also we are trying to integrate Ant Design for it's awesome components .
From the docs of Ant Design I have created a wrapper around AppWrapper of React-boilerplate like this -
import { LocaleProvider } from 'antd';
import enUS from 'antd/lib/locale-provider/en_US';
return (
<LocaleProvider locale={enUS}>
<AppWrapper>
...
</AppWrapper>
</LocaleProvider>
);
And it's working perfect for antd components.
I wanted to know how can I use this with i18n of react-boilerplate . Or if this above way is a correct way of doing this ?

This is in general the correct way of using LocaleProvider, yes, but...
You do have to be a careful when mixing an i18n librarys wrapper with antd's LocaleProvider, if you want language changes to propagate to both.
In the case of React-Boilerplate the locale is stored in Redux. For antd to be able to update locale when the app does, <LocaleProvider> must be inserted inside the Redux provider, otherwise it will not have access to the locale state.
So your app.js needs to become something like:
<Provider store={store}>
<LanguageProvider messages={messages}>
<LocaleProvider locale={...}>
<Router ... />
</LocaleProvider>
</LanguageProvider>
</Provider>,
Unfortunately this is not enough, since antd's LocaleProvider does not take a simple locale id string as its argument, but rather an object with the locale info itself.
This means that it is not possible to just insert <LocaleProvider> in the wrapper chain as above. You must create you own component that takes the connects to the Redux locale prop and feeds <LocaleProvider> with the correct locale object based on the string id from Redux.
Here is some (untested) code that I ripped out of a project with a similar structure and adapted to Intl (it was using i18next instead of Intl). Should give you an idea of one way to do it.
import React from 'react';
import { Router } from 'react-router';
import { Provider, connect } from 'react-redux';
import en_US from 'antd/lib/locale-provider/en_US';
import sv_SE from 'antd/lib/locale-provider/sv_SE';
import { LocaleProvidern } from 'antd';
class AntDesignPlusRouter extends React.Component {
state = {
antd_locale: en_US
}
componentWillUpdate( next ) {
let { locale } = next
if( !locale || locale === this.props.locale ) return;
switch( locale ) {
case 'sv':
this.setState( { antd_locale: sv_SE } );
break;
default:
case 'en':
this.setState( { antd_locale: en_US } );
break;
}
}
render() {
return (
<LocaleProvider locale={this.state.antd_locale}>
<Router {...this.props} />
</LocaleProvider>
)
}
}
const WrappedAntDesignPlusRouter = connect(
function mapStateToProps( state ) {
return {
locale: state.locale
}
},
function mapDispatchToProps( dispatch ) {
return {}
}
)( AntDesignPlusRouter );
class App extends React.Component {
render() {
return (
<Provider ...>
<LanguageProvider ...>
<WrappedAntDesignPlusRouter/>
</LanguageProvider >
</Provider>
);
}
}

for me this worked as localprovider deprecated and configprovider is replaced with
Changes done in below piece of code
Week should start with Monday
day changed from Mo to M
import { Calendar, ConfigProvider } from 'antd';
import en_GB from 'antd/lib/locale-provider/en_GB';
import moment from 'moment';
import 'moment/locale/en-gb';
import React from 'react';
import './scheduler.scss';
const Scheduler = () => {
moment.locale('en-gb'); // important!
moment.updateLocale('en', {
weekdaysMin: ["M", "T", "W", "T", "F", "S", "S"]
});
return (
<div class='scheduler-dashboard'>
<ConfigProvider locale={en_GB}>
<Calendar
/>
</ConfigProvider >
</div>
);
};
export default Scheduler;

Related

React Connected Component package without host having redux

I'm new to react and I'm trying to create a connected component (redux) for internal use in the company but I don't want to force the host app to have redux. I want to avoid having my component used like this:
<Provider store={store}><MyComponent /></Provider>
I thought in using something like Higher-Order Components and Contexts, but it is just not clicking.
Even creating my own provider would be acceptable, something like:
<MyComponentProvider><MyComponent /></MyComponentProvider>
Is it possible for a host application to use a Redux Component without having redux as a dependency?
It should be possible. The component can have its own redux store. Keep it as a complete black box.
Here is a demo, consider the package directory is your npm package and we install the redux, react-redux packages in the npm package, not your host or main project.
package/combobox.tsx:
import * as React from 'react';
import { useSelector } from 'react-redux';
export const Combobox = () => {
const state = useSelector((state) => state);
console.log('state: ', state);
return <div>combobox</div>;
};
we can wrap it in a special component that initializes the store in the constructor:
package/index.tsx:
import { Component } from 'react';
import * as React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { Combobox } from './combobox';
const reducer = (state = { counter: 1 }) => {
return state;
};
export default class ComboboxConnected extends Component {
store: ReturnType<typeof createStore>;
constructor(props) {
super(props);
this.store = createStore(reducer);
}
render() {
return (
<Provider store={this.store}>
<Combobox />
</Provider>
);
}
}
Now, each Combobox component instance has its own redux store and state.
Consumer side
App.tsx:
import * as React from 'react';
import Combobox from './package/index';
import './style.css';
export default function App() {
return (
<div>
<Combobox />
</div>
);
}
See Isolating Redux Sub-Apps
stackblitz

How to switch scss/css file at runtime or switch theme in reactjs

I'm working on a multitheme website using reactjs and created .scss file for defferent theme scheme. I've googled for switch theme in react and mostaly suggested styled component but I dont't want to do this approach.
I have created context as well and switch .scss file on context value but doing .scss file import first import file changes applied only.
I assume you are using React here, so I suggest you using React Context API.
From my understanding of the documentation, the correct way of doing it is to split style-themes by components, using one stylesheet per component, e.g.
.your-component {
&--light {
background-color: light;
}
&--dark {
background-color: dark;
}
}
You Can then create Provider and Consumer for your Context, like
const { Provider, Consumer } = createContext();
The Provider would look something like this:
default class Provider extends PureComponent {
render() {
return <Provider value={this.props.theme}>{this.props.children}</Provider>;
}
}
The Consumer:
default class Consumer extends Component {
render() {
return (
<Consumer>{theme => this.props.children(defaultTo(theme, this.props.defaultTheme))}</Consumer>
);
}
}
Then, each of your components should look something like this:
import React, { PureComponent } from 'react';
import { Consumer } from '../theme-context';
import './myComponent.scss';
default class MyComponent extends PureComponent {
_renderMyComponentWithTheme = theme => {
return (
<div styleName={`class--${theme}`}>
Hello World
</table>
);
};
render() {
return <Consumer defaultTheme="light">{this._renderMyComponentWithTheme}</Consumer>;
}
}
This will then allow you to pass a theme at the top level, and all of your components will have the same theme.
import { Provider } from '../theme-context';
<Provider value="dark">
<App />
<Provider>
https://reactjs.org/docs/context.html#contextconsumer

Routing in ReactJS with props

I have one main component where I have all state.
And here I passed this states to two different components.
The problem is - I need to open this two components in two different links (<TimeTracker />, <TimeCalendar />).
Render them separately.
How can I made it with React-router? Is it possible?
Bellow is my code for main component
export default class App extends React.Component {
constructor () {
super();
this.initStorage();
this.state = {
startTime: this.getStoreItem('startTime') || 0,
currentTask: this.getStoreItem('currentTask') || '',
results: this.getStoreItem('results') || [],
calendarResults: this.getStoreItem('calendarResults') || []
};
}
/**
create an object in localStorage for timer data if it is not present
*/
initStorage () {
let data = localStorage.getItem('timeData');
if (!data) {
localStorage.setItem('timeData', JSON.stringify({}));
}
}
/**
* get item value from storage
* #param key - item name
*/
getStoreItem = (key) => {
const data = JSON.parse(localStorage.getItem('timeData'));
return data[key];
}
/**
* change item value in storage
* * #param key - item name
* #param value - new value for item
*/
setStoreItem = (key, value) => {
const data = JSON.parse(localStorage.getItem('timeData'));
data[key] = value;
localStorage.setItem('timeData', JSON.stringify(data));
this.setState({
[key]: value
});
}
render () {
const { startTime, currentTask, results, calendarResults } = this.state;
return (
<MuiThemeProvider>
<div>
<TimeTracker
results={results}
setStoreItem={this.setStoreItem}
startTime={startTime}
currentTask={currentTask} />
<TimeCalendar calendarResults={calendarResults} />
</div>
</MuiThemeProvider>
);
}
}
I am new in Routing and did not find some similar examples.
Please help to understand how to do it.
I can make routing for them, but if component do not have props.
But in my example I'm bewildered
Thank you in advance!
Here's an extract from my reactjs code that should help you out :
Router.jsx:
import React from 'react';
import { Router, Route } from 'react-router';
import createBrowserHistory from 'history/createBrowserHistory';
// route components
import { HomePage } from '../Pages/HomePage.jsx';
import { LoginPage } from '../Pages/LoginPage.jsx';
const browserHistory = createBrowserHistory();
export const renderRoutes = () => (
<Router history={browserHistory}>
<div>
<Route exact path="/" component={HomePage}/>
<Route exact path="/login" component={LoginPage}/>
</div>
</Router>
);
In this file, you start off by defining the components that will be rendered following the url adress you will visit on your page. Here is one of these two components:
HomePage.jsx :
import React, { Component } from 'react'
import { Menu, Segment } from 'semantic-ui-react'
import { AppBarEX } from '../components/Appbar.jsx'
export class HomePage extends Component {
render() {
return (
<div>
<AppBarEX />
</div>
)
}
}
HomePage is defined as the landing page in React Router thanks to the "/" path. So when you land on the website you will automatically be directed to the HomePage. In the <AppBarEX/> I use this:
import { Link } from 'react-router-dom'
<Link to = "/login">
The element allows you to define when and how you want to link to other pages, this is probably what you were looking for. In this situation the Link will send you to the login page. To place elements inside your link, you can wrap them within the Link: <Link> your element </Link>
Finally, the element you want to render in your main.jsx goes as follows :
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { renderRoutes } from './Router.jsx';
Meteor.startup(() => {
render(renderRoutes(), document.getElementById('main_body'));
});
This will allow you to render the renderRoutes defined in the router.jsx. You can find out more here:
https://github.com/reactjs/react-router-tutorial/tree/master/lessons/02-rendering-a-route
Hope this helped you out!
D.

Navigating Programmatically in React-Router v4

I couldn't wait and I jumped into using the latest alpha version of react-router v4. The all-new <BrowserRouter/> is great in keeping your UI in sync with the browser history, but how do I use it to navigate programmatically?
The router will add a history object to your component in the props hash. So in your component, simply do:
this.props.history.push('/mypath')
Here is a full example:
In App.js:
import React from 'react'
import {BrowserRouter as Router, Route} from 'react-router-dom'
import Login from './Login'
export default class App extends React.Component {
render() {
return (
<Router>
<div>
<Route exact path='/login' component={Login} />
</div>
</Router>
)
}
}
In Login.js:
import React, {PropTypes} from 'react'
export default class Login extends React.Component {
constructor(props) {
super(props)
this.handleLogin = this.handleLogin.bind(this)
}
handleLogin(event) {
event.preventDefault()
// do some login logic here, and if successful:
this.props.history.push(`/mypath`)
}
render() {
return (
<div>
<form onSubmit={this.handleLogin}>
<input type='submit' value='Login' />
</form>
</div>
)
}
}
In the past you might have used browserHistory to push a new path. This won't work with react-router v4. Instead you have make use of React's context and router's transitionTo method.
Here's a simple example:
import React from 'react';
class NavigateNext extends React.Component {
constructor() {
super();
this.navigateProgramatically = this.navigateProgramatically.bind(this);
}
navigateProgramatically(e) {
e.preventDefault();
this.context.router.transitionTo(e.target.href)
}
render() {
return (
<Link to={"/next-page"}
onClick={this.navigateProgramatically}
>Continue</Link>
);
}
}
NavigateNext.contextTypes = {
router: React.PropTypes.object
};
transitionTo is just one of available router methods. router object also contains blockTransitions(getPromptMessage), createHref(to) and replaceWith(loc) which are worth checking out.
Here's official react-router tutorial that mentions above method.
If you wanna learn more about using react's context check out the docs.
I don't have enough reputation to comment, but in answer to #singularity's question, you have to include the context properties you wish to make available on the component class' contextTypes static property.
From the React docs on context:
If contextTypes is not defined, then context will be an empty object.
In this case:
class NavigateNext extends React.Component {
// ...
static contextTypes = {
router: PropTypes.object
}
// ...
}
Unlike propTypes, contextTypes actually cause React to behave differently and is not only for typechecking.
Using withRouter will add router properties to you component, then you can access the history and use push like you did with v3:
import React from 'react';
import { withRouter } from 'react-router-dom';
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
};
this._submit = this._submit.bind(this);
}
render() {
return (
<form onSubmit={this._submit}>
<input type="text" onChange={(event) => this.setState({input: event.target.value})}/>
<button type="submit">Submit</button>
</form>
);
}
_submit(event) {
event.preventDefault();
this.props.history.push(`/theUrlYouWantToGoTo`);
}
}
export default withRouter(Form);
react-router v4 beta is released and the API changed a little bit. Instead of this.context.router.transitionTo(e.target.href) Do, this.context.router.push(e.target.href) if you are using latest version.
Link to new doc: https://reacttraining.com/react-router/#context.router
If you need to navigate outside of a component at a location that you are unable to pass in the history object from a component similar to how you would do with browserHistory in older versions you can do the following.
First create a history module
History.js:
import createBrowserHistory from 'history/createBrowserHistory'
export default createBrowserHistory();
Then when you are declaring the Router make sure to import Router from react-router and not react-router-dom (which is just a wrapper to react-router version but creates history object automatically) and pass in the history module you just created
Root.js (or wherever you do this):
import Router from 'react-router/Router'
import history from './history'
...
class Root extends Component{
render() {
return (
<Router history={history}>
...
</Router>
);
}
}
Now your application will use the custom created history you created. You can now import that history module anywhere and just do history.replace and so forth just like you would of done with browserHistory in the past.
SomeModule.js:
import history from './history';
export default ()=>{
// redirecting to login page using history without having to pass it in
// from a component
history.replace('/login')
}
Of course this is not the recommended way just as using browserHistory in the old versions was not the recommended way since things like server side rendering won't work, but if you don't care about that this can often be the right solution.
An extra benefit doing this is you could augment the history object to things lie parsed query string params like this for example:
import createBrowserHistory from 'history/createBrowserHistory'
import queryString from 'query-string';
const history = createBrowserHistory();
history.location.query = queryString.parse(history.location.search);
history.listen(() => {
history.location.query = queryString.parse(history.location.search);
});
export default history;
If you need to access history outside of components (for example in redux actions) react-router has published their original solution here.
Basically you have to create your own history object:
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
And pass it to your router:
import { Router } from 'react-router-dom';
ReactDOM.render((
<Router history={history}> // <<-- the history object
<App/>
</Router>
), document.getElementById('root'))
Note: you have to use plain Router instead of BrowserRouter or HashRouter here!
If you export the history now, you can work with it anywhere:
import history from './history';
history.push('/home');
I found using state, a ternary operator and <Redirect> worked best. I think this is also the prefered way since it is closest to the way v4 is set up.
In the constructor()
this.state = {
redirectTo: null
}
this.clickhandler = this.clickhandler.bind(this);
In the render()
render(){
return (
<div>
{ this.state.redirectTo ?
<Redirect to={{ pathname: this.state.redirectTo }} /> :
(
<div>
..
<button onClick={ this.clickhandler } />
..
</div>
)
}
In the clickhandler()
this.setState({ redirectTo: '/path/some/where' });
Hope it helps. Let me know.
use withRouter:
import React, { PropTypes } from 'react'
import { withRouter } from 'react-router'
// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
}
render() {
const { match, location, history } = this.props
return (
<div>You are now at {location.pathname}</div>
)
}
}
// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)
It is really difficult with react-router. None of the options are straight-forward. this.props.history gave me undefined. But
window.location='/mypath/';
worked for me in version 5.0.0. Don't know whether it is the right method.

React-intl multi language app: changing languages and translations storage

I have react-router app and would like to add i18n. In react-intl example root component wrapped in IntlProvider:
ReactDOM.render(
<IntlProvider locale="en">
<App />
</IntlProvider>,
document.getElementById('container')
);
But there is only one locale. How to update app for adding other languages and how is the best way to store translations?
I have faced the same problem and this is what I found out:
To change language I used solution provided here, which is basically binding IntlProvider to ReduxStore with Connect function. Also don't forget to include key to re-render components on language change. This is basically all the code:
This is ConnectedIntlProvider.js, just binds default IntlProvider(notice the key prop that is missing in original comment on github)
import { connect } from 'react-redux';
import { IntlProvider } from 'react-intl';
// This function will map the current redux state to the props for the component that it is "connected" to.
// When the state of the redux store changes, this function will be called, if the props that come out of
// this function are different, then the component that is wrapped is re-rendered.
function mapStateToProps(state) {
const { lang, messages } = state.locales;
return { locale: lang, key: lang, messages };
}
export default connect(mapStateToProps)(IntlProvider);
And then in your entry point file:
// index.js (your top-level file)
import ConnectedIntlProvider from 'ConnectedIntlProvider';
const store = applyMiddleware(thunkMiddleware)(createStore)(reducers);
ReactDOM.render((
<Provider store={store}>
<ConnectedIntlProvider>
<Router history={createHistory()}>{routes}</Router>
</ConnectedIntlProvider>
</Provider>
), document.getElementById( APP_DOM_CONTAINER ));
Next thing to do is to just implement reducer for managing locale and make action creators to change languages on demand.
As for the best way to store translations - I found many discussions on this topic and situation seems to be quite confused, honestly I am quite baffled that makers of react-intl focus so much on date and number formats and forget about translation. So, I don't know the absolutely correct way to handle it, but this is what I do:
Create folder "locales" and inside create bunch of files like "en.js", "fi.js", "ru.js", etc. Basically all languages you work with.
In every file export json object with translations like this:
export const ENGLISH_STATE = {
lang: 'en',
messages: {
'app.header.title': 'Awesome site',
'app.header.subtitle': 'check it out',
'app.header.about': 'About',
'app.header.services': 'services',
'app.header.shipping': 'Shipping & Payment',
}
}
Other files have exact same structure but with translated strings inside.
Then in reducer that is responsible for language change import all the states from these files and load them into redux store as soon as action to change language is dispatched. Your component created in previous step will propagate changes to IntlProvider and new locale will take place. Output it on page using <FormattedMessage> or intl.formatMessage({id: 'app.header.title'})}, read more on that at their github wiki.
They have some DefineMessages function there, but honestly I couldn't find any good information how to use it, basically you can forget about it and be OK.
With a new Context API I believe it's not required to use redux now:
IntlContext.jsx
import React from "react";
import { IntlProvider, addLocaleData } from "react-intl";
import en from "react-intl/locale-data/en";
import de from "react-intl/locale-data/de";
const deTranslation = {
//...
};
const enTranslation = {
//...
};
addLocaleData([...en, ...de]);
const Context = React.createContext();
class IntlProviderWrapper extends React.Component {
constructor(...args) {
super(...args);
this.switchToEnglish = () =>
this.setState({ locale: "en", messages: enTranslation });
this.switchToDeutsch = () =>
this.setState({ locale: "de", messages: deTranslation });
// pass everything in state to avoid creating object inside render method (like explained in the documentation)
this.state = {
locale: "en",
messages: enTranslation,
switchToEnglish: this.switchToEnglish,
switchToDeutsch: this.switchToDeutsch
};
}
render() {
const { children } = this.props;
const { locale, messages } = this.state;
return (
<Context.Provider value={this.state}>
<IntlProvider
key={locale}
locale={locale}
messages={messages}
defaultLocale="en"
>
{children}
</IntlProvider>
</Context.Provider>
);
}
}
export { IntlProviderWrapper, Context as IntlContext };
Main App.jsx component:
import { Provider } from "react-redux";
import { IntlProviderWrapper } from "./IntlContext";
class App extends Component {
render() {
return (
<Provider store={store}>
<IntlProviderWrapper>
...
</IntlProviderWrapper>
</Provider>
);
}
}
LanguageSwitch.jsx
import React from "react";
import { IntlContext } from "./IntlContext";
const LanguageSwitch = () => (
<IntlContext.Consumer>
{({ switchToEnglish, switchToDeutsch }) => (
<React.Fragment>
<button onClick={switchToEnglish}>
English
</button>
<button onClick={switchToDeutsch}>
Deutsch
</button>
</React.Fragment>
)}
</IntlContext.Consumer>
);
// with hooks:
const LanguageSwitch2 = () => {
const { switchToEnglish, switchToDeutsch } = React.useContext(IntlContext);
return (
<>
<button onClick={switchToEnglish}>English</button>
<button onClick={switchToDeutsch}>Deutsch</button>
</>
);
};
export default LanguageSwitch;
I've created a repository that demonstrates this idea.
And also codesandbox example.

Resources