How to make ScrollToTop component like in React Router? I would like to move page view to the top on page change.
Here's an example:
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
import ScrollToTop from "./ScrollToTop"
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <div>Hello world!</div>,
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<ScrollToTop/>
<RouterProvider router={router} />
</React.StrictMode>
);
ScrollToTop.js
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
// "document.documentElement.scrollTo" is the magic for React Router Dom v6
document.documentElement.scrollTo({
top: 0,
left: 0,
behavior: "instant", // Optional if you want to skip the scrolling animation
});
}, [pathname]);
return null;
}
How to create the same with the use of React Location (Tanstack Router)?
We can achieve the same behaviour by using useLocation() hook in the TanStack library, but the props will be different - instead of pathname it is called current.
Here's the code for ScrollToTop.js:
import {useEffect } from "react";
import {useLocation} from "#tanstack/react-location";
export default function ScrollToTop() {
const {current} = useLocation();
useEffect(() => {
document.documentElement.scrollTo({
top: 0,
left: 0,
behavior: "instant", // Optional if you want to skip the scrolling animation
});
}, [current]);
return null;
}
Related
hello i am integrating CKEditor into a react project (create next-app) when i first use functional component, in the code below, it works as expected
import React, { useState, useEffect, useRef } from 'react'
export default function MyEditor (props) {
const editorRef = useRef()
const [editorLoaded, setEditorLoaded] = useState(false)
const { CKEditor, ClassicEditor } = editorRef.current || {}
useEffect(() => {
editorRef.current = {
CKEditor: require('#ckeditor/ckeditor5-react').CKEditor,
ClassicEditor: require('ckeditor5-custom-build/build/ckeditor'),
}
setEditorLoaded(true)
}, [])
const editorConfiguration = {
....
};
return editorLoaded ? (
<CKEditor
editor={ClassicEditor}
...
/>
) : (
<div>Editor loading</div>
)
}
but when i try to translate this same code into a react class component, it fails
import React, { Component } from "react";
export default class Editor extends Component {
constructor(props) {
super(props);
this.state = {
myText: this.props.data,
EditorLoading: true,
};
this.editorConfiguration = {
...
this.editorRef = React.createRef();
}
SO is not allowing me to paste all the code *****
The following React code should highlight the component when the children have changed. This highlighting does not always happen when the change happens before the css transaction of the previous change has finished. What can I do about it? Does it have to do with React? Or is it how css works?
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import { Box } from "#mui/material";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
highlight: {
backgroundColor: "yellow",
},
transparent: {
transition: "background-color 5s linear",
},
});
const ChangeHighlight = ({ children }: any) => {
const classes = useStyles();
const [className, setClassName] = useState(classes.transparent);
useLayoutEffect(() => {
setClassName(classes.highlight);
}, [children]);
useEffect(() => {
if (className === classes.highlight) {
setClassName(classes.transparent);
}
});
return <Box className={className}>{children}</Box>;
};
export default ChangeHighlight;
When I first go to this link the header footer style is alright-
But then when I refresh or reload the page, the style is gone. How to fixed this?
Below is My router.js-
import { createWebHistory, createRouter } from "vue-router";
import Home from '../components/Home.vue';
import Organization from '../components/aboutus/Organization.vue';
const router = createRouter({
history: createWebHistory(),
base: process.env.BASE_URL,
scrollBehavior() {
return { x: 0, y: 0 }
},
routes: [
{
name:'Home',
path:'/',
component:Home
},
{
name:'Organization',
path:'/about_us/organization',
component:Organization
},
],
})
router.beforeEach((to, _, next) => {
return next()
})
// ? For splash screen
// Remove afterEach hook if you are not using splash screen
router.afterEach(() => {
// Remove initial loading
/*const appLoading = document.getElementById('loading-bg')
if (appLoading) {
appLoading.style.display = 'none'
}*/
})
export default router
Below is the main.js-
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/router'
import store from './store/index'
createApp(App).use(
router,
store,).mount('#app')
I'm trying to use mobx-rest with mobx-rest-axios-adapter and mobx-react, and I have trouble making the component rerender upon async data retrieval.
Here's my data model, in state/user.js:
import { Model } from 'mobx-rest';
class User extends Model {
url() {
return '/me';
}
}
export default new User();
This is the React component, in App.js:
import React from 'react';
import { inject, observer } from 'mobx-react';
import { apiClient } from 'mobx-rest';
import createAdapter from 'mobx-rest-axios-adapter';
import axios from 'axios';
import { compose, lifecycle, withProps } from 'recompose';
const accessToken = '...';
const API_URL = '...';
const App = ({ user }) => (
<div>
<strong>email:</strong>
{user.has('email') && user.get('email')}
</div>
);
const withInitialise = lifecycle({
async componentDidMount() {
const { user } = this.props;
const axiosAdapter = createAdapter(axios);
apiClient(axiosAdapter, {
apiPath: API_URL,
commonOptions: {
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
});
await user.fetch();
console.log('email', user.get('email'));
},
});
export default compose(
inject('user'),
observer,
withInitialise,
)(App);
It uses recompose to get the user asynchronously from an API in componentDidMount(), and once available the component is supposed to show the user email. componentDidMount() prints the email once available.
Finally this is index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import { Router } from 'react-router';
import App from './App';
import { user } from './state/user';
const documentElement = document.getElementById('ReactApp');
if (!documentElement) {
throw Error('React document element not found');
}
const browserHistory = createBrowserHistory();
const routingStore = new RouterStore();
const stores = { user };
const history = syncHistoryWithStore(browserHistory, routingStore);
ReactDOM.render(
<Provider {...stores}>
<Router history={history}>
<App />
</Router>
</Provider>,
documentElement,
);
My problem is that the component doesn't rerender once the user is retrieved and the email is available, although the console log shows that it is returned ok in the async request. I've tried playing around with mobx-react's computed, but no luck. Any ideas?
I think it will work if you change your compose order of App.js:
export default compose(
inject('user'),
withInitialise,
observer,
)(App);
According to the MobX official document,
Tip: when observer needs to be combined with other decorators or
higher-order-components, make sure that observer is the innermost
(first applied) decorator; otherwise it might do nothing at all.
I am continuously getting " Actions must be plain objects. Use custom middleware for async actions." error and I am totally stuck here. What am I doing wrong here please help me figure out and help me get out of this error.
This is my index.js file where I have integrated redux store to the app.
import "babel-polyfill";
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux'
import { composeWithDevTools } from 'redux-devtools-extension';
import { createStore, applyMiddleware, combineReducers, compose} from 'redux'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './sagas'
import { postsReducer } from './reducers/posts'
import Routes from './routes';
import './styles/style.css'
const rootReducer = combineReducers({ postsReducer })
const sagaMiddleware = createSagaMiddleware()
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(sagaMiddleware)))
sagaMiddleware.run(rootSaga)
ReactDOM.render((
<Provider store={store}>
<Router><Routes /></Router>
</Provider>
), document.getElementById('root'))
this is my saga.js
import { take, put, call, fork, select, takeEvery, all, takeLatest } from 'redux-saga/effects'
import PostApi from './api/postApi';
import { gotPosts } from './actions/celebrity';
import { POSTS } from '../types'
export function* getAllPosts () {
const posts = yield call(PostApi.getPosts, {})
console.log('postssss', posts)
yield put(gotPosts(posts.data))
}
export function* watchGetPosts () {
yield takeLatest(POSTS, getAllPosts)
}
export default function* root() {
yield all([ fork(watchGetPosts) ])
}
this is my action.js
import { POSTS } from '../../types';
export const gotPosts = (data) => {
return {
type: POSTS,
data,
}
}
export const getPosts = () => dispatch => {
dispatch(gotPosts);
}
this is component page where i dispatched action.
import React, { Component } from 'react';
import { Card, Row, Col } from 'antd';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux'
import { getPosts } from '../actions/celebrity';
const { Meta } = Card;
class MainPage extends Component {
componentDidMount () {
console.log(this.props)
this.props.getPosts();
}
render() {
return <Row type="flex" className="main" justify="center" align="between">
......
</Row>
}
}
const mapStateToProps = state => {
return {
posts: state.postsReducer
}
}
const mapDispatchToProps = dispatch => ({
getPosts: () => {
dispatch(getPosts());
},
});
export default connect(mapStateToProps, mapDispatchToProps)(MainPage);
postsReducer
export const postsReducer = (state = [], action) => {
console.log(action)
switch(action.type){
case POSTS:
return action.data;
default:
return state;
}
}
You can't dispatch function w/o middleware support.
Problem originates from mapDispatchToProps:
{
getPosts: () => { dispatch(getPosts()); }
}
tracing down to your actions.js, getPosts() returns dispatch => dispatch(gotPosts), which is actually a function not an action(plan javascript object), redux dispatch by default doesn't recognize functions, unless you use middleware to enhance it, redux thunk for example.
Since you already have redux saga for async flow, simply dispatch an action from mapDispatchToProps should be fine, also consider create separate actions to differentiate POSTS_REQUEST, POSTS_RECEIVE, POSTS_FAILURE if possible.
import {POST} from '....../actionTypes'
...
{
getPosts: () => { dispatch({ type: POST }); }
}