Using redux in NEXT.JS - javascript

In my application on Next.Js i use redux and redux saga. I want to use ssr making http requests:
export const getStaticProps = wrapper.getStaticProps(async ({ store }) => {
store.dispatch(getDataRequest());
store.dispatch(END);
await store.sagaTask.toPromise();
});
In the same time i want to get data of the above result:
const selector = useSelector((s) => s);
console.log(selector);
The issue is that, when i run the application i get an error:
Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
I used the provider but the data doesn't appear. Question: How to solve the issue in my application?
demo: https://codesandbox.io/s/awesome-butterfly-f7vgd?file=/pages/store/saga.js

this is your _app component:
function App({ Component, pageProps }) {
return (
<div>
<Provider store={makeStore}>
<Component {...pageProps} />
</Provider>
</div>
);
}
you dont need to wrap it with Provider. this is the only thing you need to do in _app.
export default wrapper.withRedux(App)
this is getStatisProps in your pages/index.js
export const getStaticProps = wrapper.getStaticProps(async ({ store }) => {
store.dispatch(getDataRequest());
store.dispatch(END);
await store.sagaTask.toPromise();
});
see store is already passed here. You will be accesing state via store.getState(). this is how it should be
export const getStaticProps = wrapper.getStaticProps(async ({ store }) => {
store.dispatch(getDataRequest());
store.dispatch(END);
await store.sagaTask.toPromise();
const state = store.getState();
return {props:{data:state}} // since you have only one reducer
});
now data will be passed as a prop to your component. if you console.log(props.data) inside the component, you should be seeing your dataReducer

Related

How to use SSR data fetching in nextjs with apollo client?

I am try to use apollo-client with nextjs. Here I want to fetch data in getServerSideProps. Suppose I have 2 components and one page-
section.tsx this is component-1
const Section = () => {
return (
<div>
Section
</div>
);
};
export default Section;
mobile.tsx this is component 2
const Mobile = () => {
return (
<div>
Mobile
</div>
);
};
export default Mobile;
Now I call this two component into home page.
index.tsx-
const Home: NextPage = () => {
return (
<Container disableGutters maxWidth="xxl">
<Section />
<Mobile />
</Container>
);
};
export default Home;
export const getServerSideProps: GetServerSideProps = async () => {
const { data } = await client.query({ query: GET_USER_LIST })
return { props: {} }
}
Here you can see that in getServerSideProps I already fetch my data.
My question is How can I directly access this data form Section component and Mobile component without passing props. I don't want to pass props, because if my component tree will be more longer, then it will be difficult to manage props.
From appollo docs, I alreay know that apollo client do the same with redux state manager. So please tell me how can I access this data from any component that already fetched in getServerSideProps. Is it possible?. If not then how can what is the solutions.
How about using context api if you want to avoid prop drilling? By putting data into context, you can access it from any child component. Get the data from the SSR and put it into the context.
Below is the example
import React, {createContext, useContext} from "react";
export default function Home({data}) {
return <DataContext.Provider value={{data}}>
<div>
<Section/>
<Mobile/>
</div>
</DataContext.Provider>
}
export async function getServerSideProps() {
const data = 'hello world' //Get from api
return {
props: {data},
}
}
function Section() {
return <div>
Section
</div>
}
function Mobile() {
const context = useContext(DataContext);
return <div>
Mobile {context.data}
</div>
}
const DataContext = createContext({});
Now, as long as your tree structure grows within the DataContext provider, each child node will have access to data in the context.
Hope this helps.

How to set up redux in react native?

My code:
function App({ isAuthenticated }) {
const dispatch = useDispatch();
const [token, setToken] = useState()
const [appLoaded, setAppLoading] = useState(false)
const routes = buysellRoutes(isAuthenticated, token);
const [fontsLoaded] = useFonts({
Inter_900Black,
})
return (
<Provider store={store}>
<ApplicationProvider {...eva} theme={eva.light}>
{ appLoaded && fontsLoaded ? routes : <PreLoadScreen />}
</ApplicationProvider>
</Provider>
)
}
function mapStateToProps(state) {
return {
isAuthenticated: state.auth.isAuthenticated,
};
}
export default connect(mapStateToProps)(App);
Error: Could not find "store" in the context of "Connect(App)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(App) in connect options.
Here the main issue is you cannot connect the main component without a provider, so create one HOC and provide the store to the whole app and then after you can connect.
Another way is, you can use context in inner components so you don't need to connect the component just provide the store to the main HOC.
You can refer to this project for the initial react native app setup
react native starter project with redux
There are some problems with your implementation. I'll try to explain them step by step:
First, you need a store.js file to create your store outside of your App.js. for more information take a look at its documentation.
Second, the App component is considered as the root component in the react structure so avoid passing the props to the root component which is clearly wrong.
Third, you need to pass the store through of <Provider store={store} > and consume the store (get store's data as a state) in the components or pages with useSelector method, and no more need to mapStateTopProps.
Note: you must define and pass the store to the Top-level component and use it inside of the children.
Note: to check the isAuthenticated property, separate your ApplicationProvider and create a component for it.
function SomeComponent() {
const isAuthenticated = useSelector(state => state.auth.isAuthenticated)
const dispatch = useDispatch();
const [token, setToken] = useState()
const [appLoaded, setAppLoading] = useState(false)
const routes = buysellRoutes(isAuthenticated, token);
return(
<ApplicationProvider {...eva} theme={eva.light}>
{ appLoaded && fontsLoaded ? routes : <PreLoadScreen />}
</ApplicationProvider>
)
}
Optionally:
You may need to dispatch some actions in your component, with react hooks it's easy with useSelector and useDispatch without mapStateToProps or mapDispatchToProps:
function SomeExampleComponent () {
const dispatch = useDispatch();
const handleToggle = () => {
// ...
dispatch({type: "TOGGLE", payload: undefiend})
// or you can dispatch some actions that you defined before:
dispatch(onToggleAction())
}
return (
<div onClick={handleToggle}>
// ...
</div>
)}

React-router's history in react-redux provider's connect function

I've connected presentational-functional component to the redux store by means of connect function.
export default connect(
state => ({tasks: state.tasks}),
dispatch => ({
getTasks: () => apiGetTasks(dispatch),
getTask: (id) => apiGetTask(dispatch, id),
})
) (TaskView)
And now I want to redirect on the other page when getTask has triggered (or even better when apiGetTask has finished). So I tried to use history in the next way:
export default connect(
state => ({tasks: state.tasks}),
dispatch => ({
getTasks: () => apiGetTasks(dispatch),
getTask: (id) => apiGetTask(() => {
const history = useHistory()
dispatch(id)
history.push('/otherPage')
}, id),
})
) (TaskView)
But it does not work with the message:
Unhandled Rejection (Error): Invalid hook call. Hooks can only be called inside of the body of a function component....
I see that it is wrong pattern probably... So, what is the best one? Do I need to pass callback from parent component and make redirect in it? Or may be I should redirect in presentational component (but it looks strange for presentational component)
Or may be I should call dispatch(switchToPage()) in apiGetTask's promise and implement redirection in the application component, basing on the value?
What is the best solution in such case?
And one related question yet: Is it regular to use api calls in such manner as above?
Thank you, in advance!
Well.
Now you can assess my solution:
---index.js----
export let history = null;
const DefaultComponent = ()=> {
history = useHistory()
return <div style={{visibility: 'hidden'}}/>
}
ReactDOM.render(
<React.StrictMode>
<Provider store={createAppStore()}>
<Router>
<DefaultComponent/>
<Dashboard />
</Router>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
---Tasks.js----
import {history} from './index.js'
export default connect(
state => ({tasks: state.tasks}),
dispatch => ({
getTasks: () => apiGetTasks(dispatch),
getTask: (id) => apiGetTask(dispatch, id, ()=>history.push('/CreateTask')),
})
) (TaskView)
Don't hesitate to criticise. Any proposals are welcome!

React Context Does Not Propagate Changes to Other Consumers After Successful Provider Update

This is my first React Context implementation. I am using Gatsby and in my layout.js I added Context (with objects and handler function) that successfully gets passed to Consumer:
import AppContext from "./AppContext"
const Layout = ({ children }) => {
let doAuthenticate = () => {
authState = {
isAuth: authState.isAuth === true ? false : true,
}
}
let authState = {
isAuth: false,
doAuthenticate: doAuthenticate
}
return (
<>
<AppContext.Provider value={authState}>
<main>{children}</main>
</AppContext.Provider>
</>
)
I successfully execute function in Consumer:
<AppContext.Consumer>
{ value =>
<button onClick={value.doAuthenticate}Sign in</button>
}
</AppContext.Consumer>
I also see the value in doAuthenticate successfully gets updated.
However, another Consumer that is listening to Provider does not update the value. Why?
When you use Gatsby, each instance of the Page will we wrapped with the Layout component and hence you will see that instead of creating one Context that is shared between pages, you end up creating multiple contexts.
Now multiple contexts cannot communicate with each other
The solution here is to make use of wrapRootElement api in gatsby-ssr.js and gatsby-browser.js to wrap all your pages with a single layout component
import React from "react";
import Layout from "path/to/Layout";
const wrapRootElement = ({ element }) => {
return (
<Layout>
{element}
</Layout>
)
}
export { wrapRootElement };

React createBottomTabNavigator with redux store access

I'm trying to have dynamic tab navigator that is based on data on jwt but i can't figure out how to access redux data from it
this is what i have:
const Navigation = ({ jwt }) => {
console.log('jwt:', jwt);
return createBottomTabNavigator({
FeedStack,
WorkStack,
ProfileStack,
RequestStack,
});
}
const mapStateToProps = ({
common: { jwt },
}) => ({
jwt,
});
export default connect(mapStateToProps)(Navigation);
jwt is available but for Navigation is not a valid component so i get this error:
Warning: Functions are not valid as a React child. This may happen if you return a Co
mponent instead of <Component /> from render. Or maybe you meant to call this functio
how can i access props while having valid component to pass to redux?
I think you should put createBottomTabNavigation in a div tag.

Categories