I am trying to use Reach Router to navigate to a result url after a form submit. Using props.navigate takes be to the desired url, but no component is rendered (I am trying to render <Result/> after the query.
To add to the complexity, I would like to pass the personsResult prop to Result
Here is an outline of my current approach.
const App = () => {
return (
<div>
<Router>
<Search path="/" />
<List path="list" />
<Result path="result/:query" /> //!
</Router>
</div>
)
}
const Search = props => {
const [query, setQuery] = useState("")
const [personsResult, setPersonsResult] = useState("") //Pass to result
const handleSubmit = async e => {
e.preventDefault()
const person = await personsService.get(query)
setPersonsResult(person)
props.navigate(`result/${query}`) //!
}
return (
<div>
...
<SearchForm
handleSubmit={handleSubmit}
query={query}
setQuery={setQuery}
/>
</div>
)
}
I am open to switching to react-router-dom if there is a simpler means of solving this.
Related
I'm currently working on login functionality, and I notice on refresh it goes back to the login page. I was wondering if there was a workaround on this sample code.
I do know that the reason why is because on every render the loginStatus runs and it sets it to false, but is there a work around that ?
I am storing it into local storage as well
function app () {
const [loginStatus, setloginStatus] = useState(false)
const loginButtonSubmit ( event) => {
if (email field & password field is authenticatied){
setloginStatus(true)
navigate("/InfoPage")
}
}
useEffect(() => {
const data = window.localStorage.getItem("Test")
setloginStatus(JSON.parse(data))
}
useEffect(() => {
window.localStorage.setItem("Test", JSON.stringify(loginStatus))
}, [loginStatus])
const loginForm = () => {
<h1> login page functionality</h1>
<div onClick=(loginButtonSubmit) </div>
}
return (
<div className="app">
<Routes>
<Route path="/" element {loginForm()}
<Route exact path="/Money" element={loginStatus ? <MoneyPage /> : loginForm()} />
</Routes>
</div>
)
}
I have an axios request that allows me to get user information if he's logged and display it on his profile page.
My question is where to call it to avoid too many request and enhance optimization.
I've seen people calling it on top in App.js but I find it weird. I'm currently calling it in my container component that render UserProfile or LogForm depending if user is logged, but then a request is made each time user clicks on his profile page and I don't feel that it is the right way.
Here is my code:
Profile.tsx
const Profile = () => {
const dispatch = useDispatch();
const userId = useSelector((kinaccess: RootStateOrAny) => kinaccess.formsReducer.signInForm.success.userId);
useEffect(() => {
dispatch(Action.getUserInfo(userId));
}, [userId, dispatch]);
return <div>{userId ? <UserProfile /> : <LogForm />}</div>;
};
UserProfile.tsx
const UserProfile = () => {
const userInfo = useSelector((kinnacess: RootStateOrAny) => kinnacess.userReducer.user.success);
if (!userInfo) return null;
const { name, firstName, email } = userInfo;
return (
<section className='user-profile'>
<h3>
Hello {firstName} {name}
</h3>
<p>Is your mail adress still {email}</p>
</section>
);
};
An my Routes.tsx
const Routes = () => {
return (
<Routing>
<Route index element={<Home />} />
<Route path='contact' element={<ContactForm />} />
<Route path='profile' element={<Profile />} />
<Route path='*' element={<Error404 />} />
</Routing>
);
};
I have a small text field to filter a object array on my React app but when I type the onChange event skips the first character so it's not filtering the array correctly then when I delete the text it does not delete the first letter I wrote so to remove the filter you must reload the page.
This is my app:
function App() {
const [memes, setMemes] = useState([]);
const [filteredMemes, setFilteredMemes] = useState([]);
// const [filter, setFilter] = useState("");
useEffect(() => {
(async function getData() {
await getMemes();
})();
}, []);
const getMemes = async () => {
const results = await memeService.getMemes();
setMemes(results.data.data.memes);
setFilteredMemes(memes);
}
const handleSearch = (event) => {
const filter = event.target.value;
setFilteredMemes(memes);
filteredMemes.filter((meme) => meme.name.toUpperCase().search(filter.toUpperCase()) >= 0);
}
return (
<>
<Nav handleSearch={handleSearch} />
<Router>
<Switch>
<Route path="/:id/edit" children={<Editor />} />
<Route path="/:id/new" children={<New />} />
<Route path="/">
<div className='container'>
{filteredMemes.map(meme => <Meme key={meme.id} meme={meme} />)}
</div>
</Route>
</Switch>
</Router>
</>
)
}
export default App;
This is the Nav component that contains the input:
import './Nav.css';
const Nav = ({handleSearch}) => {
return (
<div className='nav'>
<input type='text' placeholder='Search' onChange={handleSearch} />
<a href='/'>Home</a>
</div>
)
}
export default Nav;
When I check on console the values that I'm gettin on the handleSearch function always gets the first letter as white space therefore when you erase the filter the results are no set to the full array.
There are a couple of problems here.
setMemes(results.data.data.memes); //you request React to update memes at some point in the future
setFilteredMemes(memes); // you set filteredMemes to memes, which is still [] at this point
setFilteredMemes(memes); // you request to change filteredMemes to the unfiltered version (again at some later point)
filteredMemes.filter((meme) => meme.name.toUpperCase().search(filter.toUpperCase()) >= 0); //filter creates a new array, not assigned to anything
Should be
setMemes(results.data.data.memes);
setFilteredMemes(results.data.data.memes);
setFilteredMemes(memes.filter((meme) => meme.name.toUpperCase().search(filter.toUpperCase()) >= 0));
I am using "react-scripts": "4.0.2" and all my components are React Hooks. My logic involves nested routing but the end result is not rendered.
App.js:
<BrowserRouter>
<div className="App">
<TopNav />
<Home />
</div>
</BrowserRouter>
Home.js:
<Switch>
<Route path="/" component={Questions} />
</Switch>
Questions.js
const displayQuestion = (qid) => {
props.history.push({ pathname: "/question/" + qid });
};
//questions is an array of objects
const questionsBlocks = questions.map((quest, i) => {
return (
<QBlock
key={i + 1}
qno={i + 1}
displayQuestion={displayQuestion.bind(this, quest.qid)}
/>
);
});
return (
<div>
<h1>Questions</h1>
{questionsBlocks}
<Switch>
<Route
path="/question/:id"
render={(props) => <Question {...props} questions={questions} />}
/>
</Switch>
</div>
);
QBlock will only render buttons that will call displayQuestion on being clicked
QBlock.js:
return (
<div className="block" onClick={props.displayQuestion}>
<h1>{props.qno}</h1>
</div>
);
Question.js:
const [question, setQuestion] = useState();
const loadQuestion = () => {
console.log(props);
if (props.match.params.id) {
console.log("load called");
const qid = props.match.params.id;
const index = props.questions.findIndex((quest) => quest.qid == qid);
setQuestion(props.questions[index]);
}
};
// componentDidMount with react hooks
useEffect(() => {
console.log("Mounted");
loadQuestion();
}, []);
// componentDidUpdate with react hooks
useEffect(() => {
console.log("Updated");
loadQuestion();
}, [props.match.params.id]); //Even tried with only props
return (
<div className="Quest">
<div className="question">{question.question}</div>
<div className="options">{question.answerChoices}</div>
</div>
);
Neither of the useEffect of Question.js is not executing still I am getting the following error.
Basically, question needs to be initialized
const [question, setQuestion] = useState(null);
And another thing you need to do is to check the value of question before using it
return (
<div className="Quest">
{question && question.question && <div className="question">{question.question}</div>}
{question && question.answerChoices && <div className="options">{question.answerChoices}</div>}
</div>
);
As Vince said.. I had defined useState like const [question, setQuestion] = useState(); instead of const [question, setQuestion] = useState({});
My main functional component performs a huge amount of useQueries and useMutations on the child component hence I have set it as React.memo so as to not cause re-rendering on each update. Basically, when new products are selected I still see the old products because of memo.
mainFunction.js
const [active, setActive] = useState(false);
const handleToggle = () => setActive(false);
const handleSelection = (resources) => {
const idsFromResources = resources.selection.map((product) => product.variants.map(variant => variant.id));
store.set('bulk-ids', idsFromResources); //loal storage js-store library
handleToggle
};
const emptyState = !store.get('bulk-ids'); // Checks local storage using js-store library
return (
<Page>
<TitleBar
title="Products"
primaryAction={{
content: 'Select products',
onAction: () => {
setActive(!active)
}
}}
/>
<ResourcePicker
resourceType="Product"
showVariants={true}
open={active}
onSelection={(resources) => handleSelection(resources)}
onCancel={handleToggle}
/>
<Button >Add Discount to Products</Button> //Apollo useMutation
{emptyState ? (
<Layout>
Select products to continue
</Layout>
) : (
<ChildComponent />
)}
</Page>
);
ChildComponent.js
class ChildComponent extends React {
return(
store.get(bulk-ids).map((product)=>{
<Query query={GET_VARIANTS} variables={{ id: variant }}>
{({ data, extensions, loading, error }) => {
<Layout>
// Query result UI
<Layout>
}}
</Query>
})
)
}
export deafult React.memo(ChildComponent);
React.memo() is useful when your component always renders the same way with no changes. In your case you need to re-render <ChildComponent> every time bulk-id changes. So you should use useMemo() hook.
function parentComponent() {
... rest of code
const bulkIds = store.get('bulk-ids');
const childComponentMemo = useMemo(() => <ChildComponent ids={bulkIds}/>, [bulkIds]);
return <Page>
... rest of render
{bulkIds ?
childComponentMemo
:(
<Layout>
Select products to continue
</Layout>
)}
</Page>
}
useMemo() returns the same value until buldIds has not changed. More details about useMemo() you can find here.