I am just starting working with react and I am trying to use a web sdk. With client side code, the library is very easy to use: Add its script to the page header and, when the document is ready, it provides you with an object that has properties, methods and events. My goal would be to have access to that object methods and properties inside react. Is it even possible?
In the code below AddScript will create an object called Stuff on the client side and using the browser dev tools I can confirm that the object exists and it not null. How do I use Staff in React?
export class MyStuff extends Component {
constructor(props) {
super(props);
this.state = {
};
}
async componentDidMount() {
try {
await AddScript();
...
Sorry if I wasted anyone's time with this question:
window.Staff
has the required object.
Related
I am new in react and i come to this new syntax please help me to understand it better.
class Car extends React.Component {
constructor() {
super();
this.state = {color: "red"};
}
render() {
return <h2>I am a {this.state.color} Car!</h2>;
}
}
root.render(<Car color="red"/>);
I have created a class component and as in object oriented programing we should create the instance of class to use it but here we are not initiating a class instance we just write the name of class in jsx language.
can anyone help me here what's happening behind the scene.
Actually react take cares of it on its own. We dont need to create instance while using recactjs.
please read the explanation here in offcial doc of reactjs.
(There mentioned below sentence.)
"Function components don’t have instances at all. Class components
have instances, but you never need to create a component instance
directly—React takes care of this."
https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html
React is a library and does it so you don't have to worry about it.
The syntax you just saw is JSX. It is just syntactic sugar for React.CreateElement. And when you go to the React source code you can see that a constructor is actually called.
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
Here ReactElement is a factory method. It creates an object and returns that. But you never have to worry about it.
It is like worrying about addEventListener. How do you know it actually works? :D
PS: There are no real classes in JS. ES6 classes are just an abstraction and are actually created using functions only.
When a user navigate to my site, I need to initialize the React web app as follow:
If the incoming user has never requested the web app, I want to set the UI language to browser default (navigator.language).
If the incoming user has already visited the site and chosen a prefered language (lang stored in the localStorage), I want to init the UI with this language.
If the incoming user has an account and is already connected (token available in localStorage), I want to auto-connect him and render the app accordingly : login button transformed into a welcome message, UI language set to user preference.
To do so, I'm using React Context API and a defaultUser object.
defaultUser: init a default user
const defaultUser = {
language: 'en_EN',
isConnected: false
}
Context: create a default context
export const AppContext = createContext({
connectedUser: defaultUser,
})
Provider: create the provider with default context
export function AppProvider({ children }: any) {
[...]
const provider = {
connectedUser
}
return (
<AppContext.Provider value={provider}>
{children}
</AppContext.Provider>
)
}
App: init the provider during app start up
export class App extends Component {
static contextType = AppContext
render() {
return (
<AppProvider>
<AppContainer />
</AppProvider>
)
}
}
AppContainer: render the app
export class AppContainer extends Component {
static contextType = AppContext
componentDidMount() {
/** If user is not connected, verify if a stored session exists and use it to connect user */
if (!this.context.connectedUser.isConnected) {
[...do things...]
}
}
The whole mecanism works well except an annoying thing : the web app is systematically initialized with default user values, until the AppContainer::componentDidMount() do the real init job.
This is causing a sort of flickering effect.
I'm struggeling for 2 days on how to fix that, trying to perform Context init before <AppContainer /> rendering, and I'm stuck.
Any recommandations?
EDIT :
For clarity, I'm adding a diagram. Currently :
React App is rendered at start.
Context is initialized at start with default value.
Context is updated when end is reached.
React App is rendered again when end.
Any layout change during these two steps (UI language, UI modification based on user permissions) are clearly visible to the user and generate a sort of flickering.
I found sort of a solution by simply conditionning <AppContainer/> loading, postponing it to the end of the sequence. However instead of having flickering I have now a lag and other unwanted side effects.
The goal would be to differ all the sequence before React Act is rendered, and after Window is available. Then dynamically create the Context, then render all.
I think the point would be resolved if I could dynamically create the AppContext and pass a variable to createContext() during App constructor() or maybe componentWillMount() (not sure when Window is available), but then TypeScript get into play with types issues and I'm still stuck.
You didn't share the code that initializes the context, but I suspect you put the default value to be either a hardcoded value, or navigator.language and therefore experience the flickering. I'd like to suggest two ways to solve this:
Solution 1
Perhaps instead of having a hardcoded default context you could generate the default context programmatically by accessing localStorage.get('lang') or similar? There is a slight drawback to this solution though: You will be mixing concerns of react and the browser, but I think in this case it's an alternative to consider, because it's very simple and obvious to the reader.
Solution 2
Alternatively, when calling ReactDOM.render you could pass down whatever you need from localStorage as a prop to your application and so you keep the browser related logic separate from the pure React stuff.
I hope this makes sense.
Here's my follow-up after Amit suggestions, in case it can help anyone else.
Init Context with functions
Instead of initializing defaultUser with hard-coded values and update it later, I set directly it with a function returning navigator.lang as suggested. This solved the flickering issue on UI labels.
Init data before RectDOM.render
However I still had flickering on UI components for which I have to get the appropriate state from an API call.
Eg, if the incoming user has a valid session token stored in localStorage, the Login button must be disabled. Before doing so, I need to make sure the session token is valid by an async call to the API. I didn't find a way to have it «awaited» by the Context init which seems to be synchronous.
That's where Amit second suggestion get into play. Instead of struggling finding a solution inside React, I did necessary processing before ReactDOM.render, then passing stuffs as props to <Apps/>.
This works pretty well to get and pass the data...
Except that Context API didn't setSate anymore as soon as any of its data was refering to an object from outside the Context. In other word using function calls is ok to init (probably by val), but reference to external objects breaks setState.
Conclusion
As my project is still in early stage, this gave me the chance to get rid of Context API, do the proper init as required, and code the props/states progagation with basic React.
i tried to update the sound array which i imported from other component every time it is changed. But however, it only fire componentDidMount() only once and it won't run again. Down below is my code on the problem:
//sound array from another component
import { soundArray } from "./CreateRecord";
export default class RecordingList extends React.Component {
constructor() {
super();
this.currentSoundArray = [];
}
componentDidMount() {
console.log(this.currentSoundArray);
this.getCurrentArray();
}
getCurrentArray() {
this.currentSoundArray = soundArray;
}
render(){
...
}
currently when i view the component, the componentDidMound will run once and console the sound array. At first, the sound array is empty:
[]
However, after i put value in the sound array and comeback to view the component, it wont print the console and it won't update the value of this.currentSoundArray
My expected result should be the currentSoundArray will be changed and print to the console every time the soundArray has been changed in another component. for example:
[]
[1,2]
[1,2,4]
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree). Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
It runs only once.
What you are trying to do is access currentSoundArray value when it is updated from another component, now that you will not be able to do traditionally.
Also componentDidMount fires only once when the component is first initialized and rendered.
Solution 1
A better way of doing this is using something like React Redux to manage your application state, this way you would be able to access the states from any component throughout your application.
I have just finished setting up a boiler plate template for this exact thing, if you would like to check it out its on Github :)
Solution 2
If you are not interested in Redux then i would suggest you use react context for this, it will solve your issue as well. you can check out many examples online for example this uses context to share a snackbar across components.
Hope this Helps!
Is there a way to retrieve the data send with Express JS render() API in a React component?
The express data is being sent over, but the only level I can catch it is in the pug template, but I want to transfer or catch it in a React component so I can retrieve more data from database inside the component.
The main issue is that the data I want to send over is originates from passport, after checking if the user is being authenticated.
This is the express route
response.render(path.resolve('views', 'account.pug'), {user: request.user});
and this is part of the pug template
body
#navbar
#account
in which I render this component
class Account extends React.Component
{
constructor(props)
{
super(props);
}
componentWillMount()
{
console.log(this.props);
}
render()
{
return(
<div class='account'>
</div>
);
}
}
I want to know if there is an elegant way to do this, without cookie manipulation or other workarounds.
If not, what would you propose the best way should be to send the data from backend into the React component?
You need to bring your data across the HTTP response, which basically means either header, cookie or body in some way or the other. Technically body is probably easiest. In this case something like a data attribute on the #account element which contains all necessary information and is then read at the time you initialise your react components. If you work with a state container like redux you might want to add the user data to the initial state that you might already transfer.
Just to reiterate the result of the comment back and forth. It might not even be necessary to give data to the client side in this specific case because it's data about a logged in user which should be in the server-side session. So creating an endpoint to fetch the data of the "current user" might be enough to fetch it in the frontend without sharing the user id specifically.
You should render the data directly into the Component's constructor. The unescaped interpolation from pug will handle this without the need for any secondary API call back to the server.
class Account extends React.Component
{
constructor(props)
{
super(props);
this.state.user = !{JSON.stringify(user)};
}
...
Our app does something very similar with Vue.js, it is very efficient and very high performance from the user's perspective.
Note: my reactjs knowledge is limited, so this.state.user might not be the exact place you need it rendered. Still, I am confident this the place where you should drop the data into React on the server.
I'm using a lightweight ORM to connect my react app with an external service... This package returns objects of your model and allows you to perform operations directly against them. While this is really awesome, I'm struggle to figure out how I can include these objects in state and still follow the "never modify state directly" tenant of react.
If I had a component that updated the account name, would it be acceptable to do something like this?
interface IAppState {
account: Account
}
class App extends React.Component<{}, IAppState> {
constructor(props) {
super(props);
this.state = {
account: new Account()
}
}
//set the new name and update the external service
public updateAccount = (newName: string)=>{
account.name = newName; //REDFLAG!!!
acc.update().then(()=>{
this.setState({ account: this.state.account })
})
}
//retrieve our account object from external service
public componentDidMount() {
const qParams = queryString.parse(window.location.search);
Account.get(qParams.externalId).then((acc)=>{
this.setState({account: acc})
})
}
render() {
return <NameEditor handleClick={this.updateAccount} account={a} />
}
}
I guess I could avoid mutating state by initiating a blank ORM object, copying the properties over, sending the update and then setting state, but this seems like a major pain.. Especially since these ORM objects can contain child ORM objects that I'd like to be able to modify as well.
Is the way I'm mutating state above "dangerous" or "bad form"???
Update
Did some reading and it definitely seems like this is probably bad form and might be navigated elegantly using the react/addons... However, what if the ORM call has a side effect on the object? For example, calling insert sets the objects external id field.
public updateAccount = (newName: string)=>{
//account.name = newName; //REDFLAG!!!
// You can use the below code to update name after it is updated
// on server.
// This will work because the object being passed here
// will be merged with Component state.
acc.update().then(()=>{
this.setState({account: {name : newName}})
})
}
Directly modifying state is not recommended as react will not come to know of the change and it will not cause a rerender.
All the diffing happens on Virtual DOM and react only updates the change attributes to Browser DOM. You can read more about the react diffing algorithm here.
React recommends to use immutable objects for setting state. you can either use Object.assign or immutable.js for this purpose which will make our life easier.
If you mutate your state object it will affect the the performance of your react component.
You can refer the below link for more information.
https://facebook.github.io/react/docs/optimizing-performance.html#examples