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.
Related
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 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.
I'll start by saying I'm very new to React and am just playing around with having components interact with each other... trying to get a sense for where state belongs and the most efficient way(s) to render changes on screen.
I have 2 sibling components, Bro and Sis that are direct children of Dad. Bro makes an HTTP request in componentWillMount to get initial values for its state. It then passes one of the pieces of data from the response (uid) back up to Dad (via a method defined in Dad) which is then passed down to Sis via props. Sis then uses this value in making ITS initial HTTP request (in componentDidUpdate) to populate ITS state.
Dad
class Dad extends Component {
state = {
uid: null
}
updateUID = id => {
this.setState({uid: id});
}
}
render() {
return (
<>
<Bro />
<Sis update={this.updateUID} />
</>
);
}
Sis
class Sis extends Component {
state = {
uid: null,
something: null,
another: null
}
componentDidUpdate() {
axios.get('example.com/endpoint2.json')
.then(res => {
/*
transform as needed and put the vales from
res.data into this.state accordingly...
*/
});
}
render () {
return <section>Component: Sis</section>;
}
}
Bro
class Bro extends Component {
state = {
uid: null,
blah: null,
blah-blah: null
}
componentWillUpdate() {
axios.get('example.com/endpoint1.json')
.then(res => {
/*
...
transform as needed and put the vales from
res.data into this.state accordingly...
*/
// pass uid back up to Dad to be passed down to Sis
this.props.update(res.data.uid);
});
}
render () {
return <section>Component: Bro</section>;
}
}
Is this Bro --> Dad --> Sis passing of data the right way to do this? This seems a bit slow and perhaps unnecessarily complicated to me... I think. The alternate ways i can think of doing it are:
have Sis make its initial HTTP request in componentWillMount and fetch the value of uid on its own. This would eliminate the need to pass it from one child to the parent to the other child, but it would involve a partially redundant query on the backend which is why I chose not to go this route.
have Dad make an HTTP request that performs 1 combined query to return the data needed by both Bro and Sis and pass it down to each accordingly. As it stands right now, Dad does not always display Bro and Sis (depending on the route). In those cases, it would be a useless HTTP request and thus definitely not right, but I'm thinking a bit of restructuring may make this viable...
perhaps nesting Dad in something like Grandpa and letting Grandpa take care of the routing while Dad fetches the data for Bro and Sis.
So I guess ultimately my question is: should I be passing data between child/adjacent/sibling components via their parent component or should the parent component be the source of the data for both children and pass it down to each accordingly?
First of all, you shouldn't be calling an HTTP request in componentWillMount(). Instead do so in componentDidMount() as stated in React docs
Your method is complete fine. However based on the container/presentational (smart/dump) components strategy you'd better do all your data fetching in <Dad /> component, then pass down the required data to the children. This way it would be so much easier to keep track of your requests and your data won't be scattered about.
An alternative is to use 3rd-party libraries such as Redux or Mobx State Tree. I'm not sure about Mobx, but what Redux does is it keeps the state outside of the components and make it available to the whole application by React context. You should be thinking about using this as it's extremely powerful and easy to learn
Last but no least, I will include a couple of posts here about container/presentational components pattern:
From Dan Abramov - The creator of Redux
Another medium post
I am trying to display an admin Dashboard in react and Meteor only if the current user is an admin.
I am calling a server method that checks the user permission and rendering the admin component only if this method returns true.
This server call is asynchronous and thus the component is not rendering, usually I manage this asynchronous call with state, but I don't want to expose anything in state here (wouldn't like someone to change the state and access the admin dashboard).
Here is the code:
export default class AdminChecker extends Component {
isItAdmin() {
// Get the id of current user
const userId = Meteor.userId();
if (userId) {
// call a server method which returns true if current user is Admin
Meteor.call('checkAdminId', userId, (err, authorized) => {
if (err) {
console.log(err);
return null;
}
return (authorized) ? <AdminDashboard /> : null;
});
}
}
render() {
return (
<div className="admin-temp-container">
{this.isItAdmin()}
</div>
);
}
}
I think I can have this logic in a parent component and send the result of isItAdmin to adminDashboard as a prop (the adminDashboard component would display information only if its props is true).
But I am insure if this is safe. Could one change the props with Chrome react developer tools or something like that?
Thanks a lot
I think there are two parts to this (and the comments are hinting at both):
First, you should not expect to be able to enforce security on the client. You must implement access control logic on the server (i.e., any API that performs an admin action must check that the user performing the action is an admin).
Once you've done that (and perhaps you already have), then you likely need to use props or state to store whether or not the user is an admin (just like you would store any other data in your app).
The key point is that once you enforce security on the server, then it doesn't really matter if a user manipulates the state to get to the admin dashboard: the server will not let the user view any real data or take any actions anyway.
This is completely unrelated to server-side rendering. You can most certainly build a secure admin dashboard without server-side rendering, as long as the APIs used to fetch the admin data and perform admin actions are implementing their own access control checks.
I want to be able to make an API call in a Flummox action and transition differently depending on the response. I can pass the router into the action call but am looking for advice on a potentially better way.
UPDATE:
The correct answer is below but I wanted to add some detail to this.
I'm doing an isomorphic app that 1. needs to get data from an api and 2. may need to redirect based on the api response. Whatever I do needs to work through an express.js app and through react.
I made a small lib that does the api call and return some results. I pass it an object (query params object from express for the server-side or a similar object I create for the react-side). This lib makes the request, determines if a redirect is needed and passes back errors, path (string), redirect (boolean), and data (json).
In express, if the redirect boolean is true, I just redirect to it with the current query params. If it's false, I pass the data to flux through an action which updates a store. I then renderToString with react, serialize stores so the clint-side can bootstrap, and send a rendered page to the client.
In react, the redirect boolean isn't important, I get the response back from my lib, pass the data to my flux action, and just transition to whatever the path is. There's really no notion of redirection. Just go to the path no matter what.
Hopefully this is helpful to someone.
In my setup I have my own router module which just wraps the instance of react-router that I create at startup. That makes it easy for any part of the application to just require that module and do what it needs to.
But I would advise you not to have side effects like a call to the router inside your actions. Actions should concern themselves on mutating your application state, and nothing more. It should be possible to call the same action from anywhere in your application which needs to perform the mutation that the action encapsulates.
So if you're switching routes during an action, you're basically tying that action to that particular use case. Let's take an example. You have a todo list, with an input box at the bottom to add a new todo. For that use case, it might be useful to switch route after you saved the todo. Perhaps you switch to Recent Todos or something. But then a new use case comes along where you want to be able to add new todos during another workflow, perhaps the user is planning her week and should be able to add todos on different days. You want the same action that adds a todo, but you certainly don't want to switch routes because the user is still planning the week.
I haven't used Flummox a lot, but from my understanding your Flux object returns whatever the action returns when you trigger an action. So instead of putting the route change inside your action, make sure to return the response from the action and let your component decide if the route should be changed. Something like this:
// todo-action.js
class TodoActions extends Actions {
createMessage(todo) {
return TodoStore.saveToServer(todo);
}
}
// todo-list.js
const TodoList extends React.Component {
render() {
...
}
addTodo(todo) {
this.props.flux.addTodo(todo).then(response => {
if (response.some.prop === someValue) {
this.props.router.transitionTo(...);
}
});
}
}
That way, the action is still nicely decoupled from the route change. If you want to do the route switch in more than one place, you could encapsulate that in a addTodoAndSwitchRoute method in your Flux class.