call api and render data after states get updated - javascript

In use effect I am checking current role of user, if it is admin setAdmin(true) set the state of admin. The default admin state is false. When component gets rendered, it gets rendered with admin as false and state gets updated in sometime. I want to call api and render data after state gets updated.
useEffect(async () => {
//Authenticates User through their email
let firebaseId;
const user = await firebase.auth().currentUser;
if (user) {
let getUserProfile = async () => {
let loggedInUser = await axios.get(
`${config.API_URL}/data/profile/${user.uid}`
);
if (loggedInUser.data.role == "admin") {
setAdmin(true);
}
};
getUserProfile();
}
}, []);

You can use useEffect hook in above case.
Ex:
useEffect(() => {
// Call your API
}, [admin]);
Above hook will call when admin state updated.

Related

Simulate actioncable websocket receive in webdriverIo

Is there a way during webdriverio runtime to simulate an actioncable receive?
I am using a fork of the package action-cable-react called actioncable-js-jwt for Rails actioncable js connections. Both of these packages are no longer maintained, but actioncable-js-jwt was the only actioncable for react package I could find that supported jwt authentication. I am building an app in my company's platform and jwt authentication is required.
The problem I am running into is that I have a react component which dispatches a redux action to call an api. The api returns a 204, and the resulting data is broadcasted out from Rails to be received by the actioncable connection. This triggers a dispatch to update the redux store with new data. The component does actions based on new data compared to the initial value on component load, so I cannot simply just set initial redux state for wdio - I need to mock the actioncable receive happening.
The way the actioncable subscription is created is:
export const createChannelSubscription = (cable, receivedCallback, dispatch, channelName) => {
let subscription;
try {
subscription = cable.subscriptions.create(
{ channel: channelName },
{
connected() {},
disconnected(res) { disconnectedFromWebsocket(res, dispatch); },
received(data) { receivedCallback(data, dispatch); },
},
);
} catch (e) {
throw new Error(e);
}
return subscription;
};
The receivedCallback function is different for each channel, but for example the function might look like:
export const handleUpdateRoundLeaderWebsocket = (data, dispatch) => {
dispatch({ type: UPDATE_ROUNDING_LEADER, round: data });
};
And the redux state is used here (code snippets):
const [currentLeader, setCurrentLeader] = useState(null);
const userId = useSelector((state) => state.userId);
const reduxStateField = useSelector((state) => state.field);
const onChange = useCallback((id) => {
if (id !== currentLeader) {
if (id !== userId && userId === currentLeader) {
setShow(true);
} else {
setCurrentLeader(leaderId);
}
}
}, [currentLeader, userId]);
useEffect(() => {
onChange(id);
}, [reduxStateField.id, onChange]);
Finally, my wdio test currently looks like:
it('has info dialog', () => {
browser.url('<base-url>-rounding-list-view');
$('#my-button').click();
$('div=Continue').click();
// need new state after the continue click
// based on new state, might show an info dialog
});
Alternatively, I could look into manually updating redux state during wdio execution - but I don't know how to do that and can't find anything on google except on how to provide initial redux state.

React : make backend API call when user is authenticated, use LocalStorage if not

I'm working on a small note taking app with React and Node.js (Express). If the user is authenticated I make API calls to the backend to fetch, create, update, delete notes persisted in a MongoDB database. If he's not, the notes are stored in localStorage. I have an AuthContext with login, logout and signup functions.
I can know if the user is loggedIn with my useAuth() custom hook in my AuthContext :
const { user } = useAuth();
And I have a separate file to make the API calls that I use in my components (getNotes, createNotes ...)
I fetch my notes in the useEffect hook
React.useEffect(() => {
const notes: Note[] = getNotes();
setNotes(notes);
}, []);
And I render my notes like this (simplified)
{notes.length > 0 && (
<ul>{notes.map(renderNote)}</ul>
)}
const renderNote = (note) => {
return (
<Note note={note} />
);
};
My question is what would be a good practice to implement the different behaviors (API calls or localStorage) ?
I can add a parameter isLoggedIn to the functions and add an if statement inside the function like this (simplified version) :
const getNotes = (isLoggedIn) => {
if (isLoggedIn) {
return notes = fetch("/notes")
} else {
return notes = localStorage.getItem("notes")
}
}
But this does not look like something clean to do if I have do to this in every function.
Thanks in advance for your help
Here's something you could do. I think I'd suggest you create the idea of some store that implements a simple getter/setter interface, then have your useAuth hook return the correct store depending on the auth state. If authenticated, then your hook returns the remote store. If not, then it returns the local storage store. But your store looks the same to your component no matter whether it's a local or remote store.
Now your code can just call get/set on the store and not care about where your info is stored or even whether the user is logged in. A main goal is to avoid having a lot of if (loggedIn) { ... } code all over your app.
Something like...
const useLocalStorageStore = () => {
const get = (key) => {
return localStorage.getItem(key);
};
const set = (key, value) => {
// I append 'local' here just to make it obvious the
// local store is in use in this example
localStorage.setItem(key, `${value} local`);
};
return { get, set };
};
// This contrived example uses localStorage too to make my example easier,
// but you'd add the fetch business to your get/set methods
// here in this remote store.
const useRemoteStore = () => {
const baseUrl = "http://localhost/foo/bar";
const get = async (key) => {
//return fetch(`${baseUrl}/${key}`);
// really should fetch here, but for this example use local
return localStorage.getItem(key);
};
const set = async (key, value) => {
// I append 'remote' here just to make it obvious the
// remote store is in use in this example
localStorage.setItem(key, `${value} remote`);
};
return { get, set };
};
const useAuth = () => {
// AuthContext is your source of loggedIn info,
// however you have it available in your app.
const { login, logout, loggedIn } = React.useContext(AuthContext);
const authedStore = useRemoteStore();
const unauthedStore = useLocalStorageStore();
const store = loggedIn ? authedStore : unauthedStore;
return { login, logout, loggedIn, store };
};
At this point, the store has all you need to get or set key values. Then you can use it in your components with...
const MyComponent = () => {
const { loggedIn, login, logout, store } = useAuth();
const setNotesValue = async (value) => {
// Your store handles communicating with the correct back end.
await store.set("notes", value);
};
const getNotesValue = async () => {
// Your store handles communicating with the correct back end.
const value = await store.get("notes");
};
return (
<div>Your UI...</div>
);
};
Here's a sandbox to demo this: https://codesandbox.io/s/gallant-gould-v1qe0

Reactjs state update after localstorage data changes

I have the user inside localstorage, when user logouts the localstorage data becomes NULL. When user logins, the localstorages fills with user's data but to check this my userEffect in App.js do not reflect any change.
i have signUp
dispatch(signin(form, history));
history.push("/"); //go back to App.js file
in Navbar the user data changes
const Navbar = (props) => {
const [user, setUser] = useState(JSON.parse(localStorage.getItem("profile")));
const logout = () => {
dispatch({ type: "LOGOUT" });
dispatch({
type: "EMPTY_CART",
});
history.push("/");
setUser(null);
};
now at App.js i have
const user = JSON.parse(localStorage?.getItem("profile"));
const getCartItems = useCallback(async () => {
if (user) {
console.log("Yes user exixts");
dispatch(getEachUserCart(user?.result?._id));
} else {
console.log("No user exist");
}
}, []); //
useEffect(() => {
getCartItems();
}, [getCartItems]);
Now if u look above, after dispatching signUp action, i come back to App.js but here the useEffect don't run nor it checks if user have changed.
Hey – looks like you have a missing dependency issue. You need to have it like so
const getCartItems = useCallback(async () => {
if (user) {
console.log("Yes user exixts");
dispatch(getEachUserCart(user?.result?._id));
} else {
console.log("No user exist");
}
}, [user]);
Otherwise, this function will never be redeclared with the latest user value. Kindly let me know if that helps

auth.onAuthStateChanged not triggering after auth.currentUser.updateProfile in Firebase

I have a component updating the user's profile pic:
const Updater = () => {
const updateProfilePic = async (photoURL) => {
await auth.currentUser.updateProfile({ 'photoURL': photoURL });
}
}
I have a second component detecting changes in the user's state:
const StateChangesDetector = () => {
auth.onAuthStateChanged( user => {
if(user)
console.log('User changed state', JSON.stringify(user));
});
}
The problem is that auth.onAuthStateChanged() is not triggering after the execution of updateProfile(). Thus, I'm getting the old user's state and the old profile picture.
How can I force to trigger auth.onAuthStateChanged() after updating the user's profile picture?
Try the reload() function on the currentUser object to get updated information. Note that it's asynchronous and returns a promise. If it doesn't trigger the listener (as it's not really a "state" change, just a refresh of data), I suspect you might have to access the firebase.auth().currentUser again after the returned promise resolves to see new data.

Next/Apollo: How to correctly update apollo cache if the relevant query was run in getInitialProps

I'm using nextjs and apollo (with react hooks). I am trying to update the user object in the apollo cache (I don't want to refetch). What is happening is that the user seems to be getting updated in the cache just fine but the user object that the component uses is not getting updated. Here is the relevant code:
The page:
// pages/index.js
...
const Page = ({ user }) => {
return <MyPage user={user} />;
};
Page.getInitialProps = async (context) => {
const { apolloClient } = context;
const user = await apolloClient.query({ query: GetUser }).then(({ data: { user } }) => user);
return { user };
};
export default Page;
And the component:
// components/MyPage.jsx
...
export default ({ user }) => {
const [toggleActive] = useMutation(ToggleActive, {
variables: { id: user.id },
update: proxy => {
const currentData = proxy.readQuery({ query: GetUser });
if (!currentData || !currentData.user) {
return;
}
console.log('user active in update:', currentData.user.isActive);
proxy.writeQuery({
query: GetUser,
data: {
...currentData,
user: {
...currentData.user,
isActive: !currentData.user.isActive
}
}
});
}
});
console.log('user active status:', user.isActive);
return <button onClick={toggleActive}>Toggle active</button>;
};
When I continuously press the button, the console log in the update function shows the user active status as flipping back and forth, so it seems that the apollo cache is getting updated properly. However, the console log in the component always shows the same status value.
I don't see this problem happening with any other apollo cache updates that I'm doing where the data object that the component uses is acquired in the component using the useQuery hook (i.e. not from a query in getInitialProps).

Categories

Resources