Do I need to use Redux or Context API - javascript

I have an application where users log in first as usual. My app has several screens which are navigated by react-native-navigation.
On every screen other than login, I need to know which user is using my app since the content is specialized by his/her uniqueID. I get that uniqueID when the user successfully login but I don't know how to pass this uniqueID to other screens.
Do I need to use Redux or context API in order to handle this problem or is there another way to pass this data between screens back and forth without changing the project?
Here is my App.js:
import React, { Component, PropTypes } from 'react';
import { AppNavigator } from './components/Navigator';
class App extends React.Component {
render() {
return (
<AppNavigator />
);
}
}
export default App;
Here is my Navigator component:
const Stack = createStackNavigator({
Main: { screen: MainScreen },
Login: {screen: LoginScreen},
Profile: {screen: ProfileScreen},
NewSurvey: {screen: NewSurveyScreen},
},
{
initialRouteName: 'Login',
headerMode: 'none',
navigationOptions: {
headerVisible: false,
gesturesEnabled: false,
}
})
export const AppNavigator = createAppContainer(Stack);

To pass data to other screens you can use React Navigation navigate method and passing some data inside of it.
i don't know how you stored you Data whether using a database like Realm or SQLite etc... but whenever you fetch data from there and you want to pass it to other screens do this like below:
this.props.navigation.navigate('SecondPage', {
//your data goes here
});
for example :
this.props.navigation.navigate('Homescreen', {
username: this.state.username,
});
and then you can get that data like below:
const username = navigation.getParam('username', 'Default'); //default is a value that will be used if there was no username passed

I think you are using React Navigation as per the code shared. I would suggest you implement auth flow as suggested here and use Redux to set/access the uniqueID. Redux serves a lot more stuff and is used widely for state management.
You can set uniqueID in _signInAsync() and then access it through. LocalStorage can be an option but not a feasible solution as accessing values from LocalStorage may affect app performance.

redux or react-context
“Redux is a predictable state container for JavaScript apps.”
“Context provides a way to pass data through the component tree
without having to pass props down manually at every level.”
For low-frequency updates like locale, theme changes, user authentication, etc. the React Context is perfectly fine. But with a more complex state which has high-frequency updates, the React Context won't be a good solution. Because, the React Context will trigger a re-render on each update, and optimizing it manually can be really tough. And there, a solution like Redux is much easier to implement.
When to use Context API
If you are using Redux only to avoid passing props down to deeply nested components, then you could replace Redux with the Context API
When to use Redux
Redux is a predictable state container, handling your application's logic outside of your components, centralizing your application's state, using Redux DevTools to track when, where, why, and how your application's state changed, or using plugins such as Redux Form, Redux Saga, Redux Undo, Redux Persist, Redux Logger, etc.
In This case we can use Redux.

Related

React Hooks with Redux persisting the App state

I just started learning react hooks and react-redux hooks and as far I understood everything. But one thing is keep drilling my brain, so I would like to ask more experienced developers here.
If I have more robust app, where I intend to have Redux taking care of whole state and wanna use React hooks fro side effects, do I really need React Hooks?
I have separate functional layer (containers => where all the decisions are being made with redux) and displaying layer (components => where components are dumb and obtain just data they are suppose to render)
Whats bugging me is I make a API call in initial page loading and I would like to use useEffect hook, but im not conviced I should do that when I can useSelector from redux and useDispatch.
here is the code I would like to update into hook style:
const mapStateToProps = (state) => {
return {
cities: state.weather.cities,
}
}
const mapDispatchToProps = (dispatch) => {
const fetchForUpdate = (cities) => {
return cities.map((city) => {
return dispatch({ type: FETCH_START, payload: city.name })
})
}
return {
fetchForUpdate: fetchForUpdate,
}
}
const WeatherListContainer = (props) => {
const { cities } = props
const cityData = cities.map((oneCity) => {
return (
<WeatherItemContainer
name={oneCity.name}
data={oneCity.data}
key={oneCity.name}
/>
)
})
return <WeatherList item={cityData} />
}
const enhance: Function = compose(
connect(mapStateToProps, mapDispatchToProps),
lifecycle({
componentDidMount() {
console.log(this.props.cities, 'this.props.cities')
this.props.fetchForUpdate(this.props.cities)
},
}),
)
export default enhance(WeatherListContainer)
how can I fetch with redux hooks or react hooks? Or can I combine it? like use useEffect and then save it from local store to global store? Isnt it a bit ineffective?
Redux requires a middleware such as redux-thunk to dispatch asynchronous actions (an API call). If you plan on calling an API multiple times throughout your app, it makes sense to use redux-thunk and dispatch an asynchronous action, though this dispatch might still need to occur within useEffect/componentDidMount. If you only plan on a single API call, or if a specific side effect is unique to one component, there is no need to implement the middleware. For a single API call, you can send your request within useEffect/componentDidMount and then dispatch the result with a synchronous action to the redux store, without having to ever store it in component state.
https://redux.js.org/advanced/async-actions
I think there are some confusions. React hooks are used for sideEffects where redux hooks are for using the store more efficientl. Lets consider a scenario like bellow.
You are fetching a todo list from API and wanna use it all over the app. You have multiple components and you are gonna need the todo list in every component. In that case, you will call the api either using a middleware like redux-thunk or by other means like caling it in a useEffect( which is not a good practice) and save it to redux store using redux hooks. And whenever you redux store is updated, you will need to show the data in components. How will we do that? we will use react hooks to apply the sideEffects.
Here we will get the data from redux store using redux hooks. Than in a reactHooks like useEffect we will update a state of the component using useState. So here you can see, both reactHooks and reduxHooks are completely different in terms of functionality. one is storing and serving data which is reduxHooks and another one is showing the data when ever its added or updated which is reactHooks.
Hope you will find it understandable.

React Router v6.0.0-alpha.5 history prop removal - navigating outside of react context

According to the latest v6.0.0-alpha.5 release of react router, the history prop has been removed:
https://github.com/ReactTraining/react-router/releases/tag/v6.0.0-alpha.5
Removed the <Router history> prop and moved responsibility for setting
up/tearing down the listener (history.listen) into the wrapper
components (<BrowserRouter>, <HashRouter>, etc.). <Router> is now a
controlled component that just sets up context for the rest of the
app.
Navigating within the react context is simple with the useNavigate hook.
But, how does the removal of the history prop affect programmatically navigating outside of the react context?
For example, how would we keep our history in sync in order to navigate from inside redux, or an axios/http interceptor, etc., when we no longer can pass the history object?
Current V5 implementation:
https://reacttraining.com/react-router/web/api/Router
Or, from v6 onwards is the goal to rely on navigating from within react components only?
Thanks for the question, we know this is going to come up a lot. This is a common question we've gotten for years. Please be patient with us as we begin documenting all of these kinds of things, there's a lot to do!
Short answer: Typically people use thunks for async work that leads to wanting to navigate somewhere else (after a login, after a record is created, etc.). When your thunk is successful, change the state to something like "success" or "redirect" and then useEffect + navigate:
export function AuthForm() {
const auth = useAppSelector(selectAuth);
const dispatch = useAppDispatch();
const navigate = useNavigate();
useEffect(() => {
if (auth.status === "success") {
navigate("/dashboard", { replace: true });
}
}, [auth.status, navigate]);
return (
<div>
<button
disabled={auth.status === "loading"}
onClick={() => dispatch(login())}
>
{auth.status === "idle"
? "Sign in"
: auth.status === "loading"
? "Signing in..."
: null}
</button>
</div>
);
}
A bit more explanation:
For example, how would we keep our history in sync in order to navigate from inside redux
We've always considered this bad practice and reluctantly provided the history objects as first-class API to stop having philosophical conversations about app state and the URL 😅.
But today things are a bit different. The conversation isn't just philosophical anymore but has some concrete bugs when mixed with React's recent async rendering, streaming, and suspense features. To protect react router apps from synchronization bugs with the URL (that developers can't do anything about), v6 no longer exposes the history object.
Hopefully this explanation will help:
Changing the URL is a side-effect, not state. Thunks are used to perform side-effects that eventually figure out some state for the state container but aren't used for the side-effect in and of itself (at least that's my understanding).
For example, you may want to change the focus on the page after your redux state changes. You probably wouldn't try to synchronize and control the document's focus at all times through redux actions and state. Scroll position is the same. Ultimately the user is in control of these things: they can hit the tab key, click on something, or scroll around. Your app doesn't try to own or synchronize that state, it just changes it from time to time in response to actions and state that you do control.
The URL is the same. Users can type whatever they want into the address bar, click back, forward, or even click and hold the back button to go 3 entries back! It's the same kind of state as focus and scroll positions: owned by the user. The container can't ever truly own the URL state because it can't control the actions surrounding it. Mix in React's new and upcoming features and you're gonna lose that game.
In a nutshell: Change redux state > useEffect in the UI > navigate. Hope that helps!
I solved this for my login redirect by creating a navigate hook in my LoginForm UI component, then passing it to my login action creator, and calling it when the login endpoint returns successfully. In other words...
import React from 'react'
import {useNavigate} from 'react-router-dom'
function LoginForm (props) {
// create your navigate hook in your UI component
const navigate = useNavigate()
// other stuff...
handleLogin (loginFields) {
// then pass it into your action file, and call it
// when the query returns a successful result
dispatch(login(loginFields, navigate))
}
// other stuff, and return statement...
}

Why Is React Context.Provider Necessary (Or useful)?

The reason React has contexts is to allow for multiple sibling components to share a piece of state-data. It is the go-to method for allowing two unrelated components to read/write in shared variables. The reason it is necessary is that React has no way to easily source a data value to multiple screens without actually passing that data between screens. Instead, it allows each screen access to the data when it needs it.
So... The implementation requires that a component be created, called a Context.Provider component, and then you have to wrap the components who need access to the shared data inside the Context.Provider. But why? Why on earth is that a requirement? Contexts are designed sharing data between components who aren't hierarchally related, and were required to put the components within a heirarchy to do so?
It would be 100 times more straight forward and just as effective to simply drop the requirement of using a Context.Provider, simple have the useContext function give access to a set variable by default:
// In ctx.js
import React from 'react';
export default CTX = React.createContext({val: "value"});
// In compA.js
import CTX from './ctx.js';
import {useContext} from 'react';
function A(props) {
var [context, setContext] = useContext(CTX);
console.log(context); //Logs {val: 'value'};
setContext({val: "newValue"});
}
Then later on, assuming component B renders after A:
import CTX from './ctx.js';
import {useContext} from 'react';
function B(props) {
var [context, setContext] = useContext(CTX);
console.log(context); //Logs {val: 'newValue'};
}
The above usage, if it actually worked, solves the task of "sharing data between unrelated components", and is much much simpler than requiring an entire new component be defined in the context file. This solution is better because:
1. No required restructuring of the application. You don't need to wrap components in a provider.
2. Any Components can just ask for any shared state easily, and they can set the shared state easily.
3. Easier to understand with much less code involved (One line of code for import and one line to initiate the context).
4. Doesn't sacrifice anything. This method allows for easy sharing of state between components, which is the entire reason of contexts in the first place.
Am I crazy? Is there a legitamate reason that we'd absolutely need to wrap our components up in a special component to share data?.. Why can't the shared state just exist independently? Its like they chose a bad solution... Why make every developer wrap there components in another component before using shared state, why not just let the developer use the damned shared state when they need to use it instead of jumping through a hoop? Someone please educate me.
Edit: One answer said that with my described method we wouldn't be able to access multiple contexts with a single component. That is false. It is actually easier with my described method:
// In context.js
export const CTX = React.createContext({val: "val"});
export const CTX2 = React.createContext({val2: "val2"});
// In app.js
function App(props) {
const [state, setState] = useContext(CTX);
const [state2, setState2] = userContext(CTX2);
return (<></>);
}
Easy. No need for Context.Provider. This is multiple contexts being used in one component, requiring just two calls to useContext versus wrapping your entire application in two nested contexts, which is what is what you have to do with current Context.Provider method...
Mate, answer is simple. React component only re-renders when it's props or state changes. Without Context.Provider component react will never understand when to re-render child components, thus you will have stale, render-blocked components.
The purpose for having a Context Provider wrap around children is to keep track of state and props, read on how state and props between parents and children affect each other. If there was no way for the Context Provider to keep track of its children, how would the components that use the Context be able to update(Changing parent state affects children, so there may be rerendering).
It's also important to understand React's philosophy and it's focus on components, it is a component-based library after all.
Important thing to remember:
Parent state change will affect children, so if state changes in parent, children components will be reevaluated and depending on how your components, state, and data are optimized (memo, callback, etc.) a rerender may occur, thus updating those children components as well.
Contexts Are Made To Handle All Use Cases
I've since spent more time using Contexts in my applications and have come to realize that Context.Provider is quite useful in a variety of situations. My initial complaint has merit in that often times when using Context we are simply wanting a variant of state that can be shared between components. In this common use case, Context.Provider does indeed requires us to write a bit of unnecessary boilerplate code and requires us to wrap elements in the provider so that they have access to the context.
However any time our shared state becomes a little more complicated having a dedicated Context.Provider component can make our lives a lot easier. Here is a use case to consider
Shared Data From External Sources (Post, Get)
Contexts may allow us to store any code related to the initialization of the shared state within the context itself, resulting in more easily readable and maintainable code. For example, lets say we have some user text posts on our server that are displayed by multiple components within our application, and we would also like for our users to be able to add new posts. All of this can be handled quite neatly within the Context.Provider:
import React, {useContext, useEffect, useState} from 'react';
export const PostsContext = React.createContext([]);
export default PostsContextProvider({children}) {
const [posts, setPosts] = useState([]);
function fetchPosts() {
// Here we will fetch the posts from our API, and then set the state
// stored within the Context.Provider equal to the fetched posts.
fetch('https://www.fakewebsite.com/api/posts/get', {
method: 'GET',
headers: {'Content-Type': 'application/json'}
}).then((response)=>{
// Convert response to json
return response.json();
}).then((json)=>{
// json here is the posts we fetched from the server, so we set the state
// equal to this value. This will update the state within all components
// that are using the context.
setPosts(json.posts);
})
}
useEffect(function(){
// This function will run a single time when the application is started
fetchPosts();
},[])
function addNewPost(post) {
// This is the function that will be used by the components.
// First, we will send the new post to the server so that it can store it.
fetch('https://www.fakewebsite.com/api/posts/post', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({post: post})
}).then((response)=>{
if(response.ok) {
// The server has updated its database with our new post.
// Now we just need to fetch the posts from the server again to get the updated data.
fetchPosts();
}
})
}
return (
<PostsContext.Provider
value={[posts, addNewPost]}
>
{children}
<PostsContext.Provider />
)
}
Notice that the value prop we are passing does not actually pass the state setter function directly. Instead, we pass the addNewPost function. So, when a component calls useContext(PostsContext) they will get the addNewPost function. This is extremely useful, it will allow our components to easily add a single post to the shared state, while also handling the server update! Very cool. With the solution I originally proposed, this would be impossible, because we would only ever get a simple state setting function from our useContext call.
Now, we must wrap our application in the provider to make it available to all components:
// App.js
import React from 'react';
import PostsContextProvider from './posts_context';
import MyComponent from './my_component';
import MyOtherComponent from './my_other_component';
export default function App() {
return (
<PostsContextProvider>
<MyComponent/>
<MyOtherComponent/>
</PostsContextProvider>
)
}
At this point, MyComponent and MyOtherComponent now have access to the context using the useContext hook. It is now extremely simple for the components to access the posts data and also update it with a new post.
import React, {useContext} from 'react';
import {PostContext} from './posts_context';
export default function MyComponent() {
const [posts, addPost] = useContext(PostsContext); // 'posts' will always be up to date with the latest data thanks to the context.
...
}
import React, {useContext} from 'react';
import {PostContext} from './posts_context';
export default function MyOtherComponent() {
const [posts, addPost] = useContext(PostsContext);
...
function handleAddPost(title, text) {
// Now when this component wants to add a new post,
// we just use the `addPost` function from the context.
addPost({title, text});
}
...
}
The beauty of this is that all the code related to the fetching and posting of data can be neatly contained within the provider, separated from the UI code. Each component has easy access to the posts data, and when either component adds a new post the other component will be updated with the new data.
Final Thoughts
This is just scratching the surface of the usefulness of Context.Provider. It's easy to imagine using a Context.Provider to handle persistent data storage using a method very similar to the above, replacing the fetch calls with function that store/fetch persistent data. Or even, some combination of persistent data and fetched data.
Upon revisiting my original question, it actually made me laugh. I was sort of right, there should perhaps be a way to handle simple shared state between components that does not require wrapping components in a provider and does not require any provider code at all. However, providers are just so dang useful in any kind of state management within an application that it is actually probably a good thing to force people to use them for simple shared state, because then they will have to learn about this wonderful tool.

Vue - Call a method from a different component (uses vue-routes)

Here is my base App.vue that holds the router-view:
<template>
<div>
<Navbar/>
<div class="container-fluid">
<router-view></router-view>
</div>
</div>
</template>
<script>
import Navbar from './components/Navbar.vue';
export default {
name: 'App',
components: {
Navbar
}
}
</script>
In the Navbar.vue, I have a method:
methods: {
getLoggedStatus(){
console.log('asdasd')
}
}
Lastly, I have a Login.vue that is loaded there on the router-view. I wanna acess the getLoggedStatus() from the Navbar.vue within Login.vue. How can I achieve this?
I tried putting a ref to the Navbar tag in App.vue:
<Navbar ref="navvy"/>
And calling in on the Login.vue with:
this.$refs.navvy.getLoggedStatus()
But it doesn't work.
A ref only works on children. You're rendering <Navbar> within app, so you cannot call that ref from login. You can only access this.$refs.navvy from App.vue.
There are several solutions for your problem.
Emit an event from Login to App, so App calls the method from a ref.
You can set a listener in the router-view, as:
<router-view #loggedStatus="callLoggedStatus" />
In your login, when you would want to call the navbar getLoggedStatus, you would instead emit that event:
this.$emit('loggedStatus')
And then in App.vue, you would defined a callLoggedStatus methods that call the ref:
callLoggedStatus() {
this.$refs.navvy.getLoggedStatus();
}
Given that you add the ref to the <Navbar> component in the APP template.
This solution is arguably the most similar to your proposed code, but I think it is a mess and you should avoid it, since you can end up listening to a lot of different events in your App.vue.
Use Vuex
I don't exactly know what getLoggedStatus does, but if you want to change how your navbar behaves when the user is logged in, you should probably setup a vuex store, so you register there wether the user is logged or not. Then in your navbar component you render things conditionally depending upon the user is logged or not.
#Lana's answer follows this idea, and is probably the closest to the official way to thins in Vue.
 Use an event emitter
If you want to directly communicate between components that are not in the same family, I think an event emitter is a reasonable choice. You could setup an application wide event emitter after creating the app:
const app = new Vue({...});
window.emitter = new Vue();
(in the example we use a new Vue as event emitter. There is also the 'events' module which allow to use a EventEmitter)
And then any component can use it to send messages, so Login could do:
window.emitter.$emit('user-logged', myCustomPayload);
And Navbar on the other hand could do:
window.emitter.$on('user-logged', function() {
this.getLoggedStatus();
})
This last option is not well considered in the Vue community -Vuex is preferred- but for small applications I think it is the simplest.
The dirty hack
You can always export your component to window. In the Navbar created hook you could do:
created() {
window.Navbar = this;
}
And then in Login.vue you could, at any time:
window.Navbar.getLoggedStatus()
And it will work. However, this is surely an anti pattern and can cause a lot of harm to your project maintainability if you start doing this with several components.
It looks like getLoggedStatus returns some global state of the app. Use Vuex for managing these global variables.
Create a store like this:
// Make sure to call Vue.use(Vuex) first if using a module system
const store = new Vuex.Store({
state: {
loggedStatus: 0
},
getters: {
getLoggedStatus: state => {
// to compute derived state based on store state
return state.loggedStatus
}
}
})
Use store.state.loggedStatus in any Vue-component to access the global state or use getters like store.getters.getLoggedStatus if you need to compute derived state based on store state.

Bind react component to part of redux state

I have redux store that looks something like this:
{
user: {},
alerts: [],
reports: [],
sourses: []
}
For each one of this parts of state i have a bunch of React Components wrapped in a container wich connected via react-redux. And has mapStateToProps like this
(state) => {alerts: state.alerts}
(state, ownProps) => {alert: _.filter(state, {id: ownProps.curId})}
Problem that when i for example launch some action for Alerts like CREATE_ALERT or EDIT_ALERT and redux state updated, ALL REACT COMPONENTS WILL RESPOND TO THIS CHANGE even ones that works with different parts like sources or reports.
My question: how to "bind" certain components to certain parts of a tree. So each container component WILL UPDATE ONLY WHEN APROPRIATE PART OF REDUX STATE UPDATED and ignore other changes.
Expected behavior
Dispatch CREATE_ALERT -> Alert reducer -> Redux store update -> ONLY Alert container component re-rendering.
When you are changing state in redux the whole state becomes just a new object.
Then your component is given by this new object (new reference) and re-renderes itself.
To fix this behaviour you need to add some logic to compare if your component got props with different value (not reference).
The easiest and fastest way is to use React.PureComponent. You can also override shouldComponentUpdate function and handle changes by yourself. But note that PureComponent works only with primitives (it does a shallow compare).
Check also Immutable.js which helps you with intelligent way of changing references of props.
if you use connect method, then pass only selected redux state to the component, this will prevent rendering of other components
example:
User Component:
const mapStateToProps = state =>({
users: state.users
});
export default connect(mapStateToProps)(User)
Alert Component:
const mapStateToProps = state =>({
alerts: state.alerts
});
export default connect(mapStateToProps)(Alert)
Check this out: Avoid Reconciliation
There explains what Neciu says.
Container components created with connect will always receive notifications of all updates to the store.
The responsibility for consuming these updates falls on the receiving connect component. It should contain the logic to extract the data relevant to it.

Categories

Resources