React i18n, check if translation exists? - internationalization

Is there a way to check if a locale exists on react app ?
Like a boolean true/false

Yes, you can check if your language variable exists with i18n.exists() method:
You can extract i18n from useTranslation hook.
const { t, i18n } = useTranslation();
Then call i18n.exists('my.key'), for example:
i18n.exists("common.values.active")
https://www.i18next.com/overview/api#exists

You could use onLoaded
i18next.on('loaded', function(loaded) {})
Gets fired on loaded resources.
And onFailedLoading
i18next.on('failedLoading', function(lng, ns, msg) {})
Gets fired if loading resources failed (after the in-built retry algorithm).
In case of react app you could use useTranslation hook to get ready boolean
// additional ready will state if translations are loaded or not
const { t, i18n, ready } = useTranslation('ns1', { useSuspense: false });
Personally, I am using ready in my root file could be index.js or App.js or whatever you have, and I am checking if translations is ready then I show my App

Related

adding logic to redux reducer to persist state

I am working with a react/redux learning project, where I'm building components to host headless CMS content. One part of the application is a dropdown that will select the content from all available content channels in the source CMS.
This works on the first pass, but when I navigate to another page (ie, the detail of a single CMS content item - the first page displays multiple items in a grid) it resets the state back to an initial (empty) variable.
The component is below:
import { FETCH_CHANNELS } from '../actions/types';
// set the initial state first
const initialState = {
isLoading: true,
data: [],
error: ""
}
// set the state depending on the dispatch coming in
const channelsReducer = (state = initialState, action) => {
switch(action.type) {
case FETCH_CHANNELS:
// reduce the returned state down to an array of options
// filter here to limit to searchable only
const activeChannels = [];
action.payload['channels'].forEach( el => {
if(el.isChannelSearchable) {
let singleItem = {
key: el.channelId,
value: el.channelId,
text: el.channelName,
type: el.channelType
}
activeChannels.push(singleItem);
}
});
return {...state, data: activeChannels, isLoading: false};
case "ERROR":
return {...state, error: action.msg};
default:
return state;
}
}
export default channelsReducer;
My issue here (as I see it), is the initialisation of the initialState constant at the beginning, everytime that the component is refreshed, it is set to empty again. Makes sense.
How can I persist the state that is returned in the FETCH_CHANNELS case (that action is a call to a back end api that returns all channels) so that upon the component remounting it still retains it's state?
Not sure if I have to either (quite possibly none of these are correct):
Attempt with some logic in the front end component that is calling this action, to not call it if data already exists?
Create another piece of state in the redux store and update that state from the front end component once a value from the drop down has been selected?
or 3. Try and handle it here with setting a variable in the reducer and logic to return that if necessary?
Like I said, I'm building this to try and learn a bit about react and redux, but i'm really not sure what the way to handle this is...
update, as suspected... neither of those options were correct. I was not calling the link correctly in the component generating the click event to drill into the detail content item. Implementing Link from react-router-dom was the right way to handle this.

Passing values between React components with useState

I use a React component with Apollo client as function component. The function body of my main search component looks like this:
function SearchContainer(props) {
const [begTime, setBegTime] = useState('')
const runSearch(begin_time) {
console.log('begin_time: ', begin_time) <== The printed value is Ok!
setBegTime(begin_time) <=== Use hook to set the value of begTime
console.log('begTime: ', begTime) <=== The output shows no change in begTime. Why?
}
return (
// Use SearchForm to get the search parameters.
<SearchForm
handleSearch={runSearch} <== Use SearchForm to get begin_time back into this component.
/>
// Provide the parameters from SearchForm and run the useQuery from Apollo using this parameters.
<SearchResults
begTime={begTime}
/>
)
}
The SearchForm is just a usual form as a React function component with useState hooks and calls on form submit the hanldeSearch function.
function SearchForm({handleSearch}) { <== handleSearch function from the parent component.
const handleSubmit = (begin_time) => {
handleSearch(begin_time) <== This call works and the value will be provided to the parent.
}
...
}
My idea of this code is to create 2 independent components. One component (SearchForm) should get the parameters. The other component (SearchResults) will get this parameters as arguments, run the query using useQuery and show the results.
But the useState hook does not work very well in my case. Interesting enough if I call the corresponding search form twice, I can see in the runSearch function, that the begTime has got the previous search values and not the initial value. So apparently the useState kind of works, but I want to run the search with the current values and not with the previous ones.
Is it possible at all to create such components with React hooks? It's my first big test with hooks instead of classes.
Thanks in advance
Andrej
For your question
const runSearch(begin_time) {
console.log('begin_time: ', begin_time)
setBegTime(begin_time)
console.log('begTime: ', begTime) <=== The output shows no change in begTime. Why?
}
The output shows no change in begTime. Why?
As stated in docs when we set state that is async function.
By that i mean your code will keep running and to set state react will start child process on another thread. And when its complete it will pass result to main thread. ( that you can c in useEffect or componentDidUpdate for early version).
So Main points are
at setBegTime(begin_time) async process is started
at main thread code will not wait for it
So next statement that is console.log('begTime: ', begTime) is processed and u saw no changes as in actual its value is not updated yet. React is still updating value.
Updating process is async because React dont want to main thread to wait for heavy work ( updating state is heavy process ) as if it wait then webpage will not respond untill it is completed. So it instead run that process on another thread.
For second one
you can try this
function SearchContainer(props) {
const [begTime, setBegTime] = useState('')
const [response,setResponse] = useState({})
useEffect(()=>{
const runSearch = (begin_time) {
setBegTime(begin_time)
}
},[begin_time])
// u can rather define working of handleSubmit in parent component and <br/>
// store its output in state and then pass it to another component
const handleSubmit = (begin_time) => {
resp = handleSearch(begin_time)
setResponse(resp)
}
return (
// Use SearchForm to get the search parameters.
<SearchForm
handleSearch={()=>handleSubmit()}
/>
// Provide the parameters from SearchForm and run the useQuery from Apollo using this parameters.
<SearchResults
begTime={begTime}
response={response}
/>
)
}

How to handle long async operations in Next.js to avoid slow page loading?

When using next-redux-wrapper how do I start a long asynchronous task so that it only runs on the client? I don’t want to use await on the server side since it would delay the initial page load. I’d rather set a loading flag as the task starts and show a loading indicator until it completes.
Let’s say my async operation looks like this:
function async takesLong(store) {
store.dispatch({type: “LOADING”, payload: true});
const result = await longOperation();
store.dispatch({type: “SETDATA”}, payload: data);
return store.dispatch({type: “LOADING”, payload: false});
}
I can call this in my Next page’s getInitialProps function like this:
MyPage.getInitialProps = async ({ store, isServer }) => {
const loader = takesLong(store)
if (isServer) await loader; // <-- will delay client response
return {
someprop: "some value"
};
};
This works well if the page loads on the client side. The operation starts, and my page can display a loading-spinner until the operation completes. But when started on the server side I have a long delay before the page displays at all. I’ve tried a number of approaches but can’t find one that works properly:
Starting the process on the server and not using await renders the page without the results being written to the store, so it has only set “loading” to true in the store and it never gets the data.
Passing store in my props doesn’t work - it ends up being an empty object ({ }) in the client.
Trying to run it inside my component doesn’t seem to work for a few reasons:
a) I don’t have the store object accessible in the React Component (only in getInitialProps which won’t get called on the client).
b) Even if I use actions instead of store.dispatch in the Component, when can I call it safely? I can’t do it during render since it will change the Redux state, and componentWillReceiveProps won’t get called before the first client-side render
Is there a well defined pattern for deferring a long operation to the client-side when using Next?
Do your long async task on componentDidMount, it will run only on client side.
React in SSR not runs componentDidMount lifecycle hook.
Using bound actions during componentDidMount works. Thanks to #eenagy for the suggestion. Doing things in this order seems to do what is needed:
import { bindActionCreators } from "redux";
import { setLoading, setError, setData } from "../actions";
componentDidMount() {
if (!this.props.haveData && !this.props.loading && !this.props.error) {
this.props.setLoading(true);
loadSomeData() // <-- this takes a while to complete
.then( data => {
this.props.setData(data);
this.props.setLoading(false);
})
.catch( err => {
this.props.setError(err);
this.props.setLoading(false);
});
}
}
render() {
if (this.props.loading) return (<Loading/>);
return (/*regular page*/);
}
export const mapDispatchToProps = dispatch => {
return bindActionCreators({ setLoading, setError, setData }, dispatch);
};
export default connect(mapStateToProps, mapDispatchToProps)(Component);
This way if the initial data is not already loaded (say by another page) it will get kicked off when the
component mounts and run asynchronously until the operation completes and
calls the actions in redux to cause the page to re-render.

Load an external JS library into a page using Greasemonkey

I want a translator in my Firefox. I find some code from internet. but it doesn't run in my Firefox. I have installed Greasemonkey.
function loadBingTranslator() {
script = document.createElement('script');
script.src = 'http://dict.bing.com.cn/cloudwidget/Scripts/Generated/BingTranslate_Selection_ShowIcon.js';
script.onload = initBingTranslator;
document.body.appendChild(script);
};
function initBingTranslator() {
BingCW.Init({
MachineTranslation: true,
WebDefinition: true
});
}
loadBingTranslator();
Such a script must account for the GM sandbox, and also (usually) allow time for the library to load and initialize.   See Avoid Common Pitfalls (in Greasemonkey).
So, you would use this library like so:
//--- Load the library.
var D = document;
var appTarg = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
var jsNode = D.createElement ('script');
jsNode.src = 'http://dict.bing.com.cn/cloudwidget/Scripts/Generated/BingTranslate_Selection_ShowIcon.js';
jsNode.addEventListener ("load", initBingTranslatorOnDelay, false);
appTarg.appendChild (jsNode);
//--- Allow some time for the library to initialize after loading.
function initBingTranslatorOnDelay () {
setTimeout (initBingTranslator, 666);
}
//--- Call the library's start-up function, if any. Note needed use of unsafeWindow.
function initBingTranslator () {
unsafeWindow.BingCW.Init ( {
AppID: "GM Foo",
MachineTranslation: true,
WebDefinition: true
} );
}
Issues, some specific to this question:
onload is not available; See the pitfalls. Event handlers cannot be set this way in GM. Also, addEventListener() is the best practice anyway.
Accessing JS (including libraries we load) in the page scope, requires unsafeWindow.
That app appears to want an AppID.
Sometimes, libraries like this can be loaded in the GM scope instead of the page scope, using the // #require directive.
I did not try that with this library, but with others, it may be possible.   Do not try this with untrusted libraries, as they gain extra abilities to infect your machine, once inside the GM scope.
Don't use reserved words, like "script", for variable names.
My JavaScript Console is outputting a "Component is not available"
line 10: script.onload = initBingTranslator;
So I fixed changed it to ... = initBingTranslator() because it is a function.
Now it says "BingCW is not definded"
Line 15: BingCW.Init({
MachineTranslation: true,
WebDefinition: true
});
And it is right, not sure if something is missing or this is supposed to only work in IE, I would find a Google translator solution personally (or just use an existing add-on).
Bing Dictionary hasd published a Firefox addon.
You can use it directly.

How to create custom event listener in node.js (express.js)?

I have a giant function with a lot of nested callbacks. I want to make it cleaner and easier to handle. So, I'm thinking of using custom event listeners
Like, when a function is done, in the callback, instead of putting a chunk of code, it just emits an event, and then the listener will run.
So, how to do that in node.js? I am still looking for a good example to guide me through.
You can set events like this
app.on('event:user_created', callback);
Then you can emit them
app.emit('event:user_created', data);
express.js uses EventEmitter.
You probably want to create an EventEmitter object, and call emit() on it.
I just came across this question, I'd like to chip in my 2 cents, specifically responding to Luis.
In Express, if you want the 'app' instance to listen for a custom event you would do something like:
app.on('testEvent', function () {
return console.log('responded to testEvent');
});
app.get('/test', function (req, res) {
app.emit('testEvent');
return res.status(200).end();
});
Then when that route is hit, you'd see the response in the console. Of course you can implement this as you wish.
The Node.js "events" module and the "EventEmitter" module facilitates communication between objects in Node. The EventEmitter module is at the core of Node's asynchronous event-driven architecture. Here is how you can create custom events and emit them:
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
//Event Listener
const EventListenerFunc = () => {
console.log('an event occurred!');
}
//Registering the event with the listener
myEmitter.on('eventName', EventListenerFunc);
//Emitting the event
myEmitter.emit('eventName');

Resources