I use getState to get a clientId that I need to include in every api call right now. Problem is that this interrupts data flow as the app doesn't rerender when clientId changes. Do I have to manually get the clientId in every component that I need to include it in or is there a better alternative? (clientId is also in store and is fetched first when the user logs in)
Sounds like a good candidate for the use of Context.
Here's a fictitious example of how you can set the client ID at a high level but reference it in nested components without having to query the Redux store each time using Hooks:
App
const ClientContext = React.createContext(null);
function App(props) {
return (
<ClientContext.Provider value={props.clientId}>
<MyApiComponent />
</ClientContext>
)
}
const mapStateToProps = getState => ({
clientId: getState().clientId
})
export default connect(mapStateToProps, {})(App);
So we only need to connect the App to the store to retrieve the client ID, then using Context we can make this value accessible to nested components. We can make use of useContext to pull that value in to each component
function MyApiComponent() {
const clientId = useContext(ClientContext);
...
return <MyNestedApiComponent />;
}
function MyNestedApiComponent() {
const clientId = useContext(ClientContext);
...
}
Whether it's function or class components you are using, the principle is the same - Context is used to share global state down to nested components.
Related
I have 2 sibling components home and history where home has the following function
//home
function handledata() {
const key = uuidv4();
localStorage.setItem(key, JSON.stringify(formdata));
}
I need to access local storage in the sibling history component so how do i access this key variable there
//history
const [items, setItems] = React.useState([]);
const handledata = () => {
localStorage.getItem(key);
};
Complete code of both files https://github.com/Megahedron69/testrep
You're best option is to decouple localstorage from both the components & make a it a separate module or a class. Then you can access it in both the files.
Pseudocode
class LocalStorage {
setItem(key, item) { ... }
getItem(key) { ... }
}
If you don't want to do that - Lifting the state up is the best possible solution when want to share data between sibling components.
Make a parent component of home & history & use localstorage there.
You don't need handledata function in that case.
You can just access localStorage without it:
const data = localStorage.getItem(key);
// use this data any way you want in the component
Ways to share data between states:
The parent component.
Localstorage
Global State management
Local storage is not recommended for this stuff and in your case global state doesn't make sense. Parent state is better in your case.
There are multiple things you can do:
Generate key in parent and share it in both components
Create a state in parent component in parent and then set that state in history
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.
Is it possible to access React context from a non-React helper function?
For example, I have a context provider that saves the root slug for a case study page when the app loads. I then need to build URLs, consuming the context provider at various places around my app. So I've created a helper function to do so:
components/caseStudies/index.jsx
import getCaseStudyPath from '../../lib/helpers/getCaseStudyPath';
const CaseStudies = ({ caseStudy }) => {
// caseStudy.slug === 'test-case-study'
const caseStudyPath = getCaseStudyPath(caseStudy.slug);
return (
<a href={caseStudyPath}>Test</a>
);
};
lib/helpers/getCaseStudySlug
export default (slug) => {
// Get caseStudyRootSlug from React context
return `/${caseStudyRootSlug}/${slug}`;
// For example, this returns '/case-studies/test-case-study'
};
I know I can just consumer the context provider in my React component, or even as a HOC, and just pass the value to the helper function, but I need to use this helper function throughout my app and don't want the overhead of setting up a consumer every time I want to use it.
So let's say I have this global JS function file that other components use to make rest calls, if the response is unauthorized I want it to change the state of a React component to loggedIn:false. Is this possible?
Looks like a use case of redux. You will maintain loggedIn in your store and connect whichever component requires this info with the store.
Else there are two other ways not that good which I will call hacks.
1) Maintain this in url query params and read the params in component.
2) Maintain this in sessionStorage or localStorage.
3) Maintain it in window.isLoggedIn
But since this is login related info I would avoid using these ways. For some other global, you can use above mentioned ways.
Take a look at redux here
https://redux.js.org/basics/usagewithreact
class SomeComponent extends React.Component {
...
async getData() {
await response = fnToGetData();
if (response.unauthorized) {
this.props.setAuthorizationStatus(false);
// or if you want to set authorization only in this component
// this.setState(() => ({authorized: false}));
} else {
// do sth with the data
}
}
}
Where setAuthorizationStatus lives somewhere on top of your app and is passed down through props (you can also use context to pass it down conveniently).
We have a ReactNative app that uses redux, redux-persist and a HeadlessJS task. This task needs to have access to the store. As the task fires without booting the entire app (and so has no access by default), we thought we could simply create the store inside the task as well so that it would be rehydrated by redux-persist. It turns out, however, that the store created in this way is different from the one in the app: after running, they contain different values. We tested this in several ways and it seems indeed a problem with the stores (and not with the actions for instance)
How should we access a Redux store from an HeadlessJS task?
Relevant code:
store/configure.js:
configureStore = (client) => {
const middleware = createMiddleware(client);
const finalCreateStore = applyMiddleware(thunk, middleware, logger)(createStore);
const store = finalCreateStore(rootReducer, undefined, autoRehydrate());
return store;
};
In use (both in the app and in the service):
const client = new ApiClient();
const store = configureStore(client);
client.setStore(store);
persistStore(store, {
storage: AsyncStorage,
}
In the app we simply use the Provider from react-redux to use the store, in the service we use store.dispatch.
For people looking for solution. I have found the solution in here.
The idea is to bind the store to async method.
https://github.com/react-native-kit/react-native-track-player/issues/63
Copy pasting the solution here.
// index
const store = ...
....registerHeadlessTask('TrackPlayer', () => require('event-handler.js').bind(null, store));
// event-handler.js
module.exports = async (store, data) {
if(data.type == '...') {
store.dispatch(...);
}
};
simply create the store inside the task as well so that it would be rehydrated by redux-persist.
This did indeed happen.
You created two stores (not advisable with redux) which were both hydrate, but not linked, as there is no such thing as linked redux stores.
Every time you run createStore, it's a new store. And every time you dispatch, you do that on a specific store.
Unfortunately async or multithreaded issues are not directly addressed by redux.
It would be possible though with middleware and / or store listeners to keep the two stores in sync.
But redux is also just not a mean for communication between threads (which I assume these tasks are, or you could just give the task a reference to the store once it was created or give the main app the store reference from the task).
It's more a form of Command-Query-Separation and centralized state.
You can access your store directly as reference.
Let's say you have your headless set in index.js, then you can just simply use store there like this:
import { AppRegistry } from 'react-native';
import Store from './src/Redux/Store';
import { someAction } from './src/Redux/Actions/someActions';
import App from './App';
import { name as appName } from './app.json';
const HeadlessTask = async () => {
console.log('Receiving HeadlessTask');
const someParam = await Store.getState().Something.someParam;
if (someParam) {
Store.dispatch(someAction(someParam));
} else {
Store.dispatch(someAction());
}
};
AppRegistry.registerHeadlessTask('HeadlessTask', () => HeadlessTask);
AppRegistry.registerComponent(appName, () => App);