I'm trying to push a graphql mutation into a component's props.
export default graphql(logOnMutation)(Login);
That line of code above doesn't work. No errors showing, but doesn't provide mutate function into props of component.
But if I write like that:
const logOnWithMutation = graphql(logOnMutation)(Login);
export default logOnWithMutation;
and write like this
const App = () => {
return (
<ApolloProvider client={client}>
<Fragment>
<logOnWithMutation/>
</Fragment>
</ApolloProvider>
)
}
it works perfectly.
So what am I missing? I need my first option to work
Decision:
import your Login component in your's parent component as default like that import Login from './containers/login/login'; not through the exact name like import { Login } from './containers/login/login';
Related
I started migrating one of my Next.js applications to version 13. I wanted to use the app directory with Server and Client Components. The documentation says that:
Server and Client Components can be interleaved in the same component tree. Behind the scenes, React will merge the work of both environments.
But my below attempt fails with this error:
Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
"use client";
import Comments from "./Comments";
const Post = () => {
return (
<div>
<p>This is a post</p>
<Comments />
</div>
);
};
export default Post;
export default async function Comments() {
const res = await fetch("https://jsonplaceholder.typicode.com/comments");
return <div></div>;
}
I have looked around the Internet and Stack Overflow but didn't find an answer. Any help would be appreciated.
If you continued a little bit through the documentation, you would have seen that they talk about a restriction when it comes to render a Server Component inside a Client Component:
However, in React, there's a restriction around importing Server Components inside Client Components because Server Components might have server-only code (e.g. database or filesystem utilities).
For example, importing a Server Component in a Client Component will not work:
'use client';
// ❌ This pattern will not work. You cannot import a Server
// Component into a Client Component
import ServerComponent from './ServerComponent';
export default function ClientComponent() {
return (
<>
<ServerComponent />
</>
);
}
Instead, you can pass a Server Component as a child or prop of a Client Component. You can do this by wrapping both components in another Server Component. For example:
'use client';
export default function ClientComponent({children}) {
return (
<>
{children}
</>
);
}
// ✅ This pattern works. You can pass a Server Component
// as a child or prop of a Client Component.
import ClientComponent from "./ClientComponent";
import ServerComponent from "./ServerComponent";
// Pages are Server Components by default
export default function Page() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
);
}
Following these guidelines, in your case, you should pass Comments as children to Post, like so:
"use client";
const Post = ({ children }) => {
return (
<div>
<p>This is a post</p>
{children}
</div>
);
};
export default Post;
<Post>
<Comments/>
</Post>
Trying next with layout pattern:
https://github.com/zeit/next.js/tree/canary/examples/layout-component
And the problem is that Layout component get remounted on every page change. I need to use layout component as a Container so it'll fetch data from server on every mount. How can I prevent layout to get re-mounted? Or am I missing something there?
This helped me for persistent layouts. The author puts together a function that wraps your page components in your Layout component and then passes that fetch function to your _app.js. This way the _app.js is actually the components that renders the Layout but you get to specify which pages use which layout (in case you have multiple layouts).
So you have the flexibility of having multiple layouts throughout your site but those pages that share the same layout will actually share the same layout component and it will not have to be remounted on navigation.
Here is the link to the full article
Persistent Layout Patterns in Next.js
Here are the important code snippets. A page and then _app.js
// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'
const AccountSettingsBasicInformation = () => (
<div>{/* ... */}</div>
)
AccountSettingsBasicInformation.getLayout = page => (
<SiteLayout>
<AccountSettingsLayout>{page}</AccountSettingsLayout>
</SiteLayout>
)
export default AccountSettingsBasicInformation
// /pages/_app.js
import React from 'react'
import App from 'next/app'
class MyApp extends App {
render() {
const { Component, pageProps, router } = this.props
const getLayout = Component.getLayout || (page => page)
return getLayout(<Component {...pageProps}></Component>)
}
}
export default MyApp
If you put your Layout component inside page component it will be re-remounted on page navigation (page switch).
You can wrap your page component with your Layout component inside _app.js, it should prevent it from re-mounting.
Something like this:
// _app.js
import Layout from '../components/Layout';
class MyApp extends App {
static async getInitialProps(appContext) {
const appProps = await App.getInitialProps(appContext);
return {
...appProps,
};
}
render() {
const { Component, pageProps } = this.props;
return (
<Layout>
<Component {...pageProps} />
<Layout />
);
}
}
export default MyApp;
Also, make sure you replace all the to <Link href=""></Link>, notice that only have change the Html tag to link.
I struggled because with this for many days, although I was doing everything else correctly, these <a> tags were the culprit that was causing the _app.js remount on page change
Even though this is the topic Layout being mounted again and again, the root cause of this problem is that you have some data loaded in some child component which is getting fetched again and again.
After some fooling around, I found none of these problem is actually what Next.Js or SWR solves. The question, back to square one, is how to streamline a single copy of data to some child component.
Context
Use context as a example.
Config.js
import { createContext } from 'react'
export default createContext({})
_App.js
import Config from '../Config'
export default function App({ Component, pageProps }) {
return (
<Config.Provider value={{ user: { name: 'John' }}}>
<Component {...pageProps} />
</Config.Provider>
)
}
Avatar.js
import { useContext } from 'react'
import Config from '../Config'
function Avatar() {
const { user } = useContext(Config)
return (
<span>
{user.name}
</span>
)
}
export default Avatar
No matter how you mount and dismount, you won't end up with re-render, as long as the _app doesn't.
Writable
The above example is only dealing with readable. If it's writable, you can try to pass a state into context. setUser will take care the set in consumer.
<Provider value={useState({})} />
const [user, setUser] = useContext(Config)
setUser is "cached" and won't be updated. So we can use this function to reset the user anytime in child consumer.
There're other ways, ex. React Recoil. But more or less you are dealing with a state management system to send a copy (either value or function) to somewhere else without touching other nodes. I'll leave this as an answer, since even we solved Layout issue, this problem won't disappear. And if we solve this problem, we don't need to deal with Layout at all.
I'm building a React app where I do NOT want the URL in the browser to be updated. I am NOT using 'react-router-dom' but only 'react-router' and MemoryRouter (https://reacttraining.com/react-router/web/api/MemoryRouter). The history.push() is available directly in the component statements but I wish to pass the history to children of children of these main components but the property is undefined.
Here is the Router section in main App.js (components Home and ScreeningTool can access this.props.history.push() as expected):
...
import {Route, MemoryRouter} from "react-router";
...
<MemoryRouter>
<Route exact path="/" component={Home} />
<Route path="/screening-tool" component={ScreeningTool} />
</MemoryRouter>
...
Both Home and ScreeningTool both use child component AppLink that generates a 'link' to navigate between Home and ScreeningTool like so (notice I'm passing 'history' as a prop):
Home.js:
...
<AppLink
url="/"
label="Screening Tool"
history={this.props.history}
/>
...
AppLink.js:
...
<div className="pseudo-link">
<span onClick={() => this.props.history.push(this.props.url)}>
{this.props.label}
</span>
</div>
...
The above code works. But there are children components in Home that will create their own AppLinks and also greatgrandchildren. I do NOT want to pass the history property as a component prop from Parent to Child to GrandChild components because this does not seem efficient. I have found the following stackoverflow questions but none of these options are working for me:
this.props.history.push works in some components and not others
react-router getting this.props.location in child components
I tried the newer 'userHistory' as described in the second URL above:
...
import { useHistory } from 'react-router';
...
render() {
let history = useHistory();
return (
<div className="pseudo-link">
<span onClick={() => history.push(this.props.url)}>
{this.props.label}
</span>
</div>
);
}
...
but I get Error: Invalid hook call. Hooks can only be called inside of the body of a function component..
I tried using withRouter as defined here https://reacttraining.com/react-router/web/api/withRouter but I get Error: Invariant failed: You should not use <withRouter(PseudoLink) /> outside a <Router>.
Finally, the accepted answer for this.props.history.push works in some components and not others ends with block of code export default withRouter(connect(mapStateToProps, matchDispatchToProps)(ChildView)); but does not explain where mapStateToProps or matchDispatchToProps comes from?
I'm thinking the issue is that I am using MemoryRouter and not normal/most common Router from 'reacto-router-dom'.
Can anyone help me out?
useHistory is a Hook so it should be used in a functional component, not inside a class based component.
Finally, the accepted answer for this.props.history.push works in some components and not others ends with block of code export default withRouter(connect(mapStateToProps, matchDispatchToProps)(ChildView)); but does not explain where mapStateToProps or matchDispatchToProps comes from?
-If you're not using redux then you can just use
export default withRouter(yourComponentName);
update
I've changed the AppLink component to this and it is working fine
import React from "react";
import "./AppLink.css";
import { useHistory } from 'react-router';
const AppLink = props => {
const history = useHistory();
return (
<div
className="app-link-button"
onClick={() => history.push(props.url)}
>
<span>{props.label}</span>
</div>
);
};
export default AppLink;
i was trying to migrate react-redux v5.X.X to v6.0.0 and there dosent seem to be any documentation for it.
i am using following versions :
"react": "^16.4.2"
"redux": "^4.0.0"
"react-redux": "^6.0.0"
the official change log says.
Passing store as a prop to a connected component is no longer supported. Instead, you may pass a custom context={MyContext} prop to both and . You may also pass {context : MyContext} as an option to connect.
link is here
here is my root index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { configureStore, history } from './Store';
import App from './App.hot';
import 'antd/dist/antd.min.css';
const reduxStore = configureStore();
ReactDOM.render(<App store={reduxStore} history={history} />, document.getElementById('root'));
here is my app.jsx (root component)
import React from 'react';
import PropTypes from 'prop-types';
import { Provider, connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { ConnectedRouter } from 'connected-react-router';
import Layout from './Layout';
class App extends React.Component {
static propTypes = {
store: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
render() {
const { store, profile, history } = this.props;
return (
<main className="app-wrapper">
// what i understand from change log is this part
// i need to pass context instead of store as props.
<Provider store={store}>
<ConnectedRouter history={history}>
<Layout user={profile} />
</ConnectedRouter>
</Provider>
</main>
);
}
}
function mapStateToProps(store) {
return {
...
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
...
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
as per change log i created context and passed it down to the provider
const storeContext = React.createContext(reduxStore);
here is my render function after that change
render() {
const { store, profile, history } = this.props;
return (
<main className="app-wrapper">
<Provider context={storeContext}>
<ConnectedRouter history={history}>
<Layout user={profile} />
</ConnectedRouter>
</Provider>
</main>
);
}
passing store as props to provider gives following error
Passing redux store in props has been removed and does not do anything. To use a custom Redux store for specific components, create a custom React context with React.createContext(), and pass the context object to React-Redux's Provider and specific components like: . You may also pass a {context : MyContext} option to connect
and passing as context gives following 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.
i did not find any documentation expect this redux history document here it tells all the problems and solutions for the problem in react-redux and how the context api fixed it. but i am not sure how to actually implement it in real project.
did anyone face the same issue ? or can you please tell me how exactly to implement this change.
thanks
I was able to solve the problem by actually listening to what the error message said.
there were two problems with my code
i was passing store as props to my <App /> component. which is why the first warning/error message was comming.
Passing redux store in props has been removed and does not do anything. To use a custom Redux store for specific components, create a custom React context with React.createContext(), and pass the context object to React-Redux's Provider and specific components like: . You may also pass a {context : MyContext} option to connect
to fix this simply dont pass whole redux store as props to any component
my Provider from react-redux was not the root component. the error message said
Could not find "store" in the context of "Connect(App)". Either wrap
the root component in a Provider , or pass a custom React context provider to
and the corresponding React context consumer to Connect(App) in
connect options
so i followed the second wanring in the sentence
Either wrap the root component in a Provider , or pass a custom React context
so i wrapped my main root in provider. and things started working well.
ReactDOM.render(
<Provider store={reduxStore}>
<App />
</Provider>, document.getElementById('root'),
);
I had the same problem and this is how i solved it.
const MyContext = React.createContext();
class App extends Component {
render() {
return (
<Provider store = {store} context={MyContext}>
<BrowserRouter>
<div>
<Main context={MyContext}/>
</div>
</BrowserRouter>
</Provider>
);
}
}
I have started learning basic of Context API in ReactJS.
This is a part of reactJS code which uses reactJS.
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import LeftPane from './LeftPane';
import RightPane from './RightPane';
import {SampleProvider} from './sample';
const App =()=>{
return(
<SampleProvider>
<div className="panes">
<LeftPane/>
<RightPane/>
</div>
</SampleProvider>
)
}
export default App;
ReactDOM.render(
<App/>,
document.getElementById('root')
)
RightPane.js
RightPane.js
import React from 'react';
import Receives from './Receives';
const RightPane =()=>{
return(
<div className="pane">
<Receives/>
</div>
)
};
export default RightPane;
sample.js
import React,{Component , createContext} from 'react';
const Context = createContext();
const {Provider, Consumer : SampleConsumer}=Context;
class SampleProvider extends Component{
state={
value:'default value'
}
actions={
setValue:(value)=>{
this.setState({value});
}
}
render(){
const {state,actions}=this;
const value={state,actions};
return(
<Provider value={value}>
{this.props.children}
</Provider>
)
}
}
export{
SampleProvider,
SampleConsumer
};
Receives.js
import React from 'react';
import {SampleConsumer} from './sample';
const Receives = ()=>{
return(
<SampleConsumer>
{
(sample)=>(
<div>
Value:{sample.state.value}
</div>
)
}
</SampleConsumer>
)
}
console.log(Receives);
export default Receives;
Everything is fine. I understand everything except the function in SampleConsumer
component.
function in SampleConsumer uses sample as parameter.
I tested and sample.state.value renders 'default value' and it is the value of the state which is declared in SampleProvider component.
SampleProvider passes down the state as props to Provider component. I understand
Provider can use that state. But how the parameter in SampleConsumer understands
state in SampleProvider component? I have never passed the state as props to
SampleProvider component ..(I understood so. Maybe it's wrong)
I read this documentation
https://reactjs.org/docs/context.html
but didn't understand 100%
Everything is fine. I understand everything except the function in SampleConsumer component.
You have set SampleConsumer to point to the raw Consumer output of createContext(). It will function exactly the same as the ThemeContext.Consumer example in the docs.
function in SampleConsumer uses sample as parameter. I tested and sample.state.value renders 'default value' and it is the value of the state which is declared in SampleProvider component.
You have wrapped the raw Provider output of createContext() with your SampleProvider component. As you did so, you set the Provider's context value to (initially) be:
{
state: {
value: 'default value'
},
actions: {
setValue: (value) => { this.setState({value}) }
}
}
Meaning that whenever you invoke SampleConsumer that is a child of SampleProvider, the argument in the "child as a function" will be passed that value. In other words, this would display the string representation of the object in the above snippet:
<SampleConsumer>
{ (value) => <div>{value.toString()}</div> }
</SampleConsumer>
SampleProvider passes down the state as props to Provider component. I understand Provider can use that state.
Correct - you have set Provider's value prop to be equal to an object that contains SampleProvider's state.
But how the parameter in SampleConsumer understands state in SampleProvider component?
This is exactly what the context API accomplishes. SampleConsumer has access to Provider's value prop, without needing to pass the prop through all the child elements in between. Note that your code here doesn't have anything in between, so it's a little trivial; the docs you linked provide a better example.
I have never passed the state as props to SampleProvider component ..(I understood so. Maybe it's wrong)
You passed SampleProvider's state as a prop to Provider. Provider, in turn, passed its prop down to SampleConsumer.
I think the core of the misunderstanding here is your use (or naming) of SampleProvider. I'm not sure what you're trying to do with that state, but it's not really a "Provider" anymore and makes things confusing. This is unlike your SampleConsumer, which is still the default Consumer, just renamed.