My page is divided into right and left parts. On the left side there are 3 buttons, and on the right side there is a list with 3 texts. They are in different files. I want the corresponding text to come first in the list when the button is clicked. The functionality is similar to tabs, but I don’t know how to implement it, because the components are in different files and are not connected. How can I do that?
//Buttons.js const btnArr = [ ["Togle Text 1"], ["Togle Text 2"], ["Togle Text 3"], ]; const Buttons = () => { return ( <div style={{ width: "50%" }}> {btn.map((btn, index) => ( <Button key={index} text={btn} /> ))} ); }; //Text.js const btnArr = [ ["Text 1"], ["Text 2"], ["Text 3"], ]; const Texts = () => { return ( <div style={{ width: "50%" }}> {texts.map((txt, index) => ( <Text key={index} text={txt} /> ))} ); };
Advertisement
Answer
Parent Component
You’ll want to use a useState
in a parent component of both Texts
and Buttons
. That way you can keep track of which button has been clicked, and you’ll pass Buttons
a way to update which has been clicked. You’ll also be able to pass Texts
the value of which text is currently selected.
That parent component could look like this:
const [selectedText, setSelectedText] = useState(0); return ( <div > <Buttons onSelect={setSelectedText} /> <Texts selectedText={selectedText} /> </div> );
Buttons Component
Next we’ll handle the Buttons
Component. You can see in the above codeblock we are passing Buttons
a prop called onSelect
which we’ll use to update the selectedText
state.
Here’s what that component could look like:
export const Buttons = ({ onSelect }) => { return ( <div> {btnArr.map((btn, index) => ( <Button key={index} text={btn} onClick={() => onSelect(index)} /> ))} </div> ); };
Now, whenever a button is clicked, the selectedText
state variable in the Parent
will be updated to the index of the button clicked.
Texts Component
The Texts
Component is a little bit trickier because we need to show the selected Text
before the other Text
s.
Since we are passing in selectedText
as a prop, we can use that as we are creating the list. Our Texts
component should look like this:
export const Texts = ({ selectedText }) => {
The most basic way to order the list is by placing our selectedText
item first, followed by the mapped over text elements, but with the selectedText
item filtered out. It may make more sense to look at the code:
{<Text text={texts[selectedText]} />} {texts .filter((txt, index) => index !== selectedText) .map((txt, index) => ( <Text key={index} text={txt} /> ))}
That way will work just fine, but if you don’t want to have a <Text ... />
in two places, we can avoid that by using the following code instead. The more complicated way to do this is by using sort: we can sort through the text array to order them and then map over them like this:
{texts .sort((txt, txt2) => txt === texts[selectedText] ? -1 : txt2 === texts[selectedText] ? 1 : 0 ) .map((txt, index) => ( <Text key={index} text={txt} /> ))}
I’ve put together a full example of this on CodeSandbox here:
Extending the List
The advantage of doing it this way is that you can easily add more items to the list of buttons/text. If we simply add a ["Toggle Text 4"]
to the button list and a ["Text 4"]
to the text list, you can see that everything still just works.
The CodePen example demonstrates this.
Working with Different Files
In my explanation, we worked with three separate files for our code: a parent file, Texts.js
, and Buttons.js
.
Here’s how you can use the Texts
and Buttons
component from inside the parent:
In the parent file at the top, import the other two files like this:
import { Texts } from "./Texts"; import { Buttons } from "./Buttons";
Then inside Texts.js
, make sure to have the word export
before the component is defined like this:
export const Texts = ({ selectedText }) => {
Do the same in Buttons.js
:
export const Buttons = ({ onSelect }) => {
This allows us to use code from one file in a separate file. This guide gives a bit more explanation on how that works.