diff --git a/beta/src/pages/learn/adding-interactivity.md b/beta/src/pages/learn/adding-interactivity.md index b296f16b3..d84a0a9e0 100644 --- a/beta/src/pages/learn/adding-interactivity.md +++ b/beta/src/pages/learn/adding-interactivity.md @@ -72,7 +72,7 @@ Read **[Responding to Events](/learn/responding-to-events)** to learn how to add -## State: a component's memory {/*state-a-components-memory*/} +## Stan: Pamięć komponentu {/*state-a-components-memory*/} Components often need to change what's on the screen as a result of an interaction. Typing into the form should update the input field, clicking "next" on an image carousel should change which image is displayed, clicking "buy" puts a product in the shopping cart. Components need to "remember" things: the current input value, the current image, the shopping cart. In React, this kind of component-specific memory is called state. @@ -224,7 +224,7 @@ button { -Read **[State: A Component's Memory](/learn/state-a-components-memory)** to learn how to remember a value and update it on interaction. +Read **[Stan: Pamięć komponentu](/learn/state-a-components-memory)** to learn how to remember a value and update it on interaction. diff --git a/beta/src/pages/learn/describing-the-ui.md b/beta/src/pages/learn/describing-the-ui.md index 6e5ab65be..964d207fb 100644 --- a/beta/src/pages/learn/describing-the-ui.md +++ b/beta/src/pages/learn/describing-the-ui.md @@ -237,7 +237,7 @@ Read **[JavaScript in JSX with Curly Braces](/learn/javascript-in-jsx-with-curly -## Passing props to a component {/_passing-props-to-a-component_/} +## Przekazywanie wartości do komponentu {/_passing-props-to-a-component_/} React components use _props_ to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, functions, and even JSX! @@ -304,7 +304,7 @@ export function getImageUrl(person, size = 's') { -Read **[Passing Props to a Component](/learn/passing-props-to-a-component)** to learn how to pass and read props. +Read **[Przekazywanie wartości do komponentu](/learn/passing-props-to-a-component)** to learn how to pass and read props. diff --git a/beta/src/pages/learn/passing-props-to-a-component.md b/beta/src/pages/learn/passing-props-to-a-component.md index 3a56cbc49..4ee95d852 100644 --- a/beta/src/pages/learn/passing-props-to-a-component.md +++ b/beta/src/pages/learn/passing-props-to-a-component.md @@ -1,1113 +1,1113 @@ ---- -title: Passing Props to a Component ---- - - - -React components use **props** to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions. - - - - - -* How to pass props to a component -* How to read props from a component -* How to specify default values for props -* How to pass some JSX to a component -* How props change over time - - - -## Familiar props {/*familiar-props*/} - -Props are the information that you pass to a JSX tag. For example, `className`, `src`, `alt`, `width`, and `height` are some of the props you can pass to an ``: - - - -```js -function Avatar() { - return ( - Lin Lanying - ); -} - -export default function Profile() { - return ( - - ); -} -``` - -```css -body { min-height: 120px; } -.avatar { margin: 20px; border-radius: 50%; } -``` - - - -The props you can pass to an `` tag are predefined (ReactDOM conforms to [the HTML standard](https://www.w3.org/TR/html52/semantics-embedded-content.html#the-img-element)). But you can pass any props to *your own* components, such as ``, to customize them. Here's how! - -## Passing props to a component {/*passing-props-to-a-component*/} - -In this code, the `Profile` component isn't passing any props to its child component, `Avatar`: - -```js -export default function Profile() { - return ( - - ); -} -``` - -You can give `Avatar` some props in two steps. - -### Step 1: Pass props to the child component {/*step-1-pass-props-to-the-child-component*/} - -First, pass some props to `Avatar`. For example, let's pass two props: `person` (an object), and `size` (a number): - -```js -export default function Profile() { - return ( - - ); -} -``` - -> If double curly braces after `person=` confuse you, remember [they are merely an object](/learn/javascript-in-jsx-with-curly-braces#using-double-curlies-css-and-other-objects-in-jsx) inside the JSX curlies. - -Now you can read these props inside the `Avatar` component. - -### Step 2: Read props inside the child component {/*step-2-read-props-inside-the-child-component*/} - -You can read these props by listing their names `person, size` separated by the commas inside `({` and `})` directly after `function Avatar`. This lets you use them inside the `Avatar` code, like you would with a variable. - -```js -function Avatar({ person, size }) { - // person and size are available here -} -``` - -Add some logic to `Avatar` that uses the `person` and `size` props for rendering, and you're done. - -Now you can configure `Avatar` to render in many different ways with different props. Try tweaking the values! - - - -```js App.js -import { getImageUrl } from './utils.js'; - -function Avatar({ person, size }) { - return ( - {person.name} - ); -} - -export default function Profile() { - return ( -
- - - -
- ); -} -``` - -```js utils.js -export function getImageUrl(person, size = 's') { - return ( - 'https://i.imgur.com/' + - person.imageId + - size + - '.jpg' - ); -} -``` - -```css -body { min-height: 120px; } -.avatar { margin: 10px; border-radius: 50%; } -``` - -
- -Props let you think about parent and child components independently. For example, you can change the `person` or the `size` props inside `Profile` without having to think about how `Avatar` uses them. Similarly, you can change how the `Avatar` uses these props, without looking at the `Profile`. - -You can think of props like "knobs" that you can adjust. They serve the same role as arguments serve for functions—in fact, props _are_ the only argument to your component! React component functions accept a single argument, a `props` object: - -```js -function Avatar(props) { - let person = props.person; - let size = props.size; - // ... -} -``` - -Usually you don't need the whole `props` object itself, so you destructure it into individual props. - - - -**Don't miss the pair of `{` and `}` curlies** inside of `(` and `)` when declaring props: - -```js -function Avatar({ person, size }) { - // ... -} -``` - -This syntax is called ["destructuring"](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_a_function_parameter) and is equivalent to reading properties from a function parameter: - -```js -function Avatar(props) { - let person = props.person; - let size = props.size; - // ... -} -``` - - - -## Specifying a default value for a prop {/*specifying-a-default-value-for-a-prop*/} - -If you want to give a prop a default value to fall back on when no value is specified, you can do it with the destructuring by putting `=` and the default value right after the parameter: - -```js -function Avatar({ person, size = 100 }) { - // ... -} -``` - -Now, if `` is rendered with no `size` prop, the `size` will be set to `100`. - -The default value is only used if the `size` prop is missing or if you pass `size={undefined}`. But if you pass `size={null}` or `size={0}`, the default value will **not** be used. - -## Forwarding props with the JSX spread syntax {/*forwarding-props-with-the-jsx-spread-syntax*/} - -Sometimes, passing props gets very repetitive: - -```js -function Profile({ person, size, isSepia, thickBorder }) { - return ( -
- -
- ); -} -``` - -There's nothing wrong with repetitive code—it can be more legible. But at times you may value conciseness. Some components forward all of their props to their children, like how this `Profile` does with `Avatar`. Because they don't use any of their props directly, it can make sense to use a more concise "spread" syntax: - -```js -function Profile(props) { - return ( -
- -
- ); -} -``` - -This forwards all of `Profile`'s props to the `Avatar` without listing each of their names. - -**Use spread syntax with restraint.** If you're using it in every other component, something is wrong. Often, it indicates that you should split your components and pass children as JSX. More on that next! - -## Passing JSX as children {/*passing-jsx-as-children*/} - -It is common to nest built-in browser tags: - -```js -
- -
-``` - -Sometimes you'll want to nest your own components the same way: - -```js - - - -``` - -When you nest content inside a JSX tag, the parent component will receive that content in a prop called `children`. For example, the `Card` component below will receive a `children` prop set to `` and render it in a wrapper div: - - - -```js App.js -import Avatar from './Avatar.js'; - -function Card({ children }) { - return ( -
- {children} -
- ); -} - -export default function Profile() { - return ( - - - - ); -} -``` - -```js Avatar.js -import { getImageUrl } from './utils.js'; - -export default function Avatar({ person, size }) { - return ( - {person.name} - ); -} -``` - -```js utils.js -export function getImageUrl(person, size = 's') { - return ( - 'https://i.imgur.com/' + - person.imageId + - size + - '.jpg' - ); -} -``` - -```css -.card { - width: fit-content; - margin: 5px; - padding: 5px; - font-size: 20px; - text-align: center; - border: 1px solid #aaa; - border-radius: 20px; - background: #fff; -} -.avatar { - margin: 20px; - border-radius: 50%; -} -``` - -
- -Try replacing the `` inside `` with some text to see how the `Card` component can wrap any nested content. It doesn't need to "know" what's being rendered inside of it. You will see this flexible pattern in many places. - -You can think of a component with a `children` prop as having a "hole" that can be "filled in" by its parent components with arbitrary JSX. You will often use the `children` prop for visual wrappers: panels, grids, and so on. You can explore this in more detail in [Extracting Layout Components](/learn/extracting-layout-components). - - - -## How props change over time {/*how-props-change-over-time*/} - -The `Clock` component below receives two props from its parent component: `color` and `time`. (The parent component's code is omitted because it uses [state](/learn/state-a-components-memory), which we won't dive into just yet.) - -Try changing the color in the select box below: - - - -```js Clock.js active -export default function Clock({ color, time }) { - return ( -

- {time} -

- ); -} -``` - -```js App.js hidden -import { useState, useEffect } from 'react'; -import Clock from './Clock.js'; - -function useTime() { - const [time, setTime] = useState(() => new Date()); - useEffect(() => { - const id = setInterval(() => { - setTime(new Date()); - }, 1000); - return () => clearInterval(id); - }, []); - return time; -} - -export default function App() { - const time = useTime(); - const [color, setColor] = useState('lightcoral'); - return ( -
-

- Pick a color:{' '} - -

- -
- ); -} -``` - -
- -This example illustrates that **a component may receive different props over time.** Props are not always static! Here, the `time` prop changes every second, and the `color` prop changes when you select another color. Props reflect a component's data at any point in time, rather than only in the beginning. - -However, props are [immutable](https://en.wikipedia.org/wiki/Immutable_object)—a term from computer science meaning "unchangeable." When a component needs to change its props (for example, in response to a user interaction or new data), it will have to "ask" its parent component to pass it _different props_—a new object! Its old props will then be cast aside, and eventually the JavaScript engine will reclaim the memory taken by them. - -**Don't try to "change props".** When you need to respond to the user input (like changing the selected color), you will need to "set state", which you can learn about in [State: A Component's Memory](/learn/state-a-components-memory). - - - -* To pass props, add them to the JSX, just like you would with HTML attributes. -* To read props, use the `function Avatar({ person, size })` destructuring syntax. -* You can specify a default value like `size = 100`, which is used for missing and `undefined` props. -* You can forward all props with `` JSX spread syntax, but don't overuse it! -* Nested JSX like `` will appear as `Card` component's `children` prop. -* Props are read-only snapshots in time: every render receives a new version of props. -* You can't change props. When you need interactivity, you'll need to set state. - - - - - - - -### Extract a component {/*extract-a-component*/} - -This `Gallery` component contains some very similar markup for two profiles. Extract a `Profile` component out of it to reduce the duplication. You'll need to choose what props to pass to it. - - - -```js App.js -import { getImageUrl } from './utils.js'; - -export default function Gallery() { - return ( -
-

Notable Scientists

-
-

Maria Skłodowska-Curie

- Maria Skłodowska-Curie -
    -
  • - Profession: - physicist and chemist -
  • -
  • - Awards: 4 - (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal) -
  • -
  • - Discovered: - polonium (element) -
  • -
-
-
-

Katsuko Saruhashi

- Katsuko Saruhashi -
    -
  • - Profession: - geochemist -
  • -
  • - Awards: 2 - (Miyake Prize for geochemistry, Tanaka Prize) -
  • -
  • - Discovered: - a method for measuring carbon dioxide in seawater -
  • -
-
-
- ); -} -``` - -```js utils.js -export function getImageUrl(imageId, size = 's') { - return ( - 'https://i.imgur.com/' + - imageId + - size + - '.jpg' - ); -} -``` - -```css -.avatar { margin: 5px; border-radius: 50%; min-height: 70px; } -.profile { - border: 1px solid #aaa; - border-radius: 6px; - margin-top: 20px; - padding: 10px; -} -h1, h2 { margin: 5px; } -h1 { margin-bottom: 10px; } -ul { padding: 0px 10px 0px 20px; } -li { margin: 5px; } -``` - -
- - - -Start by extracting the markup for one of the scientists. Then find the pieces that don't match it in the second example, and make them configurable by props. - - - - - -In this solution, the `Profile` component accepts multiple props: `imageId` (a string), `name` (a string), `profession` (a string), `awards` (an array of strings), `discovery` (a string), and `imageSize` (a number). - -Note that the `imageSize` prop has a default value, which is why we don't pass it to the component. - - - -```js App.js -import { getImageUrl } from './utils.js'; - -function Profile({ - imageId, - name, - profession, - awards, - discovery, - imageSize = 70 -}) { - return ( -
-

{name}

- {name} -
    -
  • Profession: {profession}
  • -
  • - Awards: {awards.length} - ({awards.join(', ')}) -
  • -
  • - Discovered: - {discovery} -
  • -
-
- ); -} - -export default function Gallery() { - return ( -
-

Notable Scientists

- - -
- ); -} -``` - -```js utils.js -export function getImageUrl(imageId, size = 's') { - return ( - 'https://i.imgur.com/' + - imageId + - size + - '.jpg' - ); -} -``` - -```css -.avatar { margin: 5px; border-radius: 50%; min-height: 70px; } -.profile { - border: 1px solid #aaa; - border-radius: 6px; - margin-top: 20px; - padding: 10px; -} -h1, h2 { margin: 5px; } -h1 { margin-bottom: 10px; } -ul { padding: 0px 10px 0px 20px; } -li { margin: 5px; } -``` - -
- -Note how you don't need a separate `awardCount` prop if `awards` is an array. Then you can use `awards.length` to count the number of awards. Remember that props can take any values, and that includes arrays too! - -Another solution, which is more similar to the earlier examples on this page, is to group all information about a person in a single object, and pass that object as one prop: - - - -```js App.js -import { getImageUrl } from './utils.js'; - -function Profile({ person, imageSize = 70 }) { - const imageSrc = getImageUrl(person) - - return ( -
-

{person.name}

- {person.name} -
    -
  • - Profession: {person.profession} -
  • -
  • - Awards: {person.awards.length} - ({person.awards.join(', ')}) -
  • -
  • - Discovered: - {person.discovery} -
  • -
-
- ) -} - -export default function Gallery() { - return ( -
-

Notable Scientists

- - -
- ); -} -``` - -```js utils.js -export function getImageUrl(person, size = 's') { - return ( - 'https://i.imgur.com/' + - person.imageId + - size + - '.jpg' - ); -} -``` - -```css -.avatar { margin: 5px; border-radius: 50%; min-height: 70px; } -.profile { - border: 1px solid #aaa; - border-radius: 6px; - margin-top: 20px; - padding: 10px; -} -h1, h2 { margin: 5px; } -h1 { margin-bottom: 10px; } -ul { padding: 0px 10px 0px 20px; } -li { margin: 5px; } -``` - -
- -Although the syntax looks slightly different because you're describing properties of a JavaScript object rather than a collection of JSX attributes, these examples are mostly equivalent, and you can pick either approach. - -
- -### Adjust the image size based on a prop {/*adjust-the-image-size-based-on-a-prop*/} - -In this example, `Avatar` receives a numeric `size` prop which determines the `` width and height. The `size` prop is set to `40` in this example. However, if you open the image in a new tab, you'll notice that the image itself is larger (`160` pixels). The real image size is determined by which thumbnail size you're requesting. - -Change the `Avatar` component to request the closest image size based on the `size` prop. Specifically, if the `size` is less than `90`, pass `'s'` ("small") rather than `'b'` ("big") to the `getImageUrl` function. Verify that your changes work by rendering avatars with different values of the `size` prop and opening images in a new tab. - - - -```js App.js -import { getImageUrl } from './utils.js'; - -function Avatar({ person, size }) { - return ( - {person.name} - ); -} - -export default function Profile() { - return ( - - ); -} -``` - -```js utils.js -export function getImageUrl(person, size) { - return ( - 'https://i.imgur.com/' + - person.imageId + - size + - '.jpg' - ); -} -``` - -```css -.avatar { margin: 20px; border-radius: 50%; } -``` - - - - - -Here is how you could go about it: - - - -```js App.js -import { getImageUrl } from './utils.js'; - -function Avatar({ person, size }) { - let thumbnailSize = 's'; - if (size > 90) { - thumbnailSize = 'b'; - } - return ( - {person.name} - ); -} - -export default function Profile() { - return ( - <> - - - - ); -} -``` - -```js utils.js -export function getImageUrl(person, size) { - return ( - 'https://i.imgur.com/' + - person.imageId + - size + - '.jpg' - ); -} -``` - -```css -.avatar { margin: 20px; border-radius: 50%; } -``` - - - -You could also show a sharper image for high DPI screens by taking [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) into account: - - - -```js App.js -import { getImageUrl } from './utils.js'; - -const ratio = window.devicePixelRatio; - -function Avatar({ person, size }) { - let thumbnailSize = 's'; - if (size * ratio > 90) { - thumbnailSize = 'b'; - } - return ( - {person.name} - ); -} - -export default function Profile() { - return ( - <> - - - - - ); -} -``` - -```js utils.js -export function getImageUrl(person, size) { - return ( - 'https://i.imgur.com/' + - person.imageId + - size + - '.jpg' - ); -} -``` - -```css -.avatar { margin: 20px; border-radius: 50%; } -``` - - - -Props let you encapsulate logic like this inside the `Avatar` component (and change it later if needed) so that everyone can use the `` component without thinking about how the images are requested and resized. - - - -### Passing JSX in a `children` prop {/*passing-jsx-in-a-children-prop*/} - -Extract a `Card` component from the markup below, and use the `children` prop to pass different JSX to it: - - - -```js -export default function Profile() { - return ( -
-
-
-

Photo

- Aklilu Lemma -
-
-
-
-

About

-

Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.

-
-
-
- ); -} -``` - -```css -.card { - width: fit-content; - margin: 20px; - padding: 20px; - border: 1px solid #aaa; - border-radius: 20px; - background: #fff; -} -.card-content { - text-align: center; -} -.avatar { - margin: 10px; - border-radius: 50%; -} -h1 { - margin: 5px; - padding: 0; - font-size: 24px; -} -``` - -
- - - -Any JSX you put inside of a component's tag will be passed as the `children` prop to that component. - - - - - -This is how you can use the `Card` component in both places: - - - -```js -function Card({ children }) { - return ( -
-
- {children} -
-
- ); -} - -export default function Profile() { - return ( -
- -

Photo

- Aklilu Lemma -
- -

About

-

Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.

-
-
- ); -} -``` - -```css -.card { - width: fit-content; - margin: 20px; - padding: 20px; - border: 1px solid #aaa; - border-radius: 20px; - background: #fff; -} -.card-content { - text-align: center; -} -.avatar { - margin: 10px; - border-radius: 50%; -} -h1 { - margin: 5px; - padding: 0; - font-size: 24px; -} -``` - -
- -You can also make `title` a separate prop if you want every `Card` to always have a title: - - - -```js -function Card({ children, title }) { - return ( -
-
-

{title}

- {children} -
-
- ); -} - -export default function Profile() { - return ( -
- - Aklilu Lemma - - -

Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.

-
-
- ); -} -``` - -```css -.card { - width: fit-content; - margin: 20px; - padding: 20px; - border: 1px solid #aaa; - border-radius: 20px; - background: #fff; -} -.card-content { - text-align: center; -} -.avatar { - margin: 10px; - border-radius: 50%; -} -h1 { - margin: 5px; - padding: 0; - font-size: 24px; -} -``` - -
- -
- +--- +title: Przekazywanie wartości do komponentu +--- + + + +Komponenty reactowe używają **właściwości** (_ang._ props, od "properties") do komunikowania się między sobą. Każdy komponent nadrzędny może przekazać informacje do własnych potomków poprzez właściwości. Właściwości mogą kojarzyć się z atrybutami HTML-owymi, jednak różnica polega na tym, że przez właściwości można przekazywać dowolne wartości javascriptowe, w tym obiekty, tablice czy funkcje. + + + + + +* Jak przekazać wartości do komponentu +* Jak odczytać właściwości komponentu +* Jak określić domyślną wartość dla właściwości +* Jak przekazać kod JSX-owy do komponentu +* Jak właściwości zmieniają się w czasie + + + +## Właściwości, które możesz już znać {/*familiar-props*/} + +Właściwości (_ang._ props) to informacje, które przekazujemy znacznikowi JSX-owemu. Na przykład, znacznikowi `` możemy przekazać właściwości `className`, `src`, `alt`, `width` czy `height`: + + + +```js +function Avatar() { + return ( + Lin Lanying + ); +} + +export default function Profile() { + return ( + + ); +} +``` + +```css +body { min-height: 120px; } +.avatar { margin: 20px; border-radius: 50%; } +``` + + + +Właściwości, które możesz przekazać do znacznika ``, są predefiniowane (ReactDOM przestrzega [standardu HTML](https://www.w3.org/TR/html52/semantics-embedded-content.html#the-img-element)). Jednak do *własnych* komponentów, np. ``, możesz przekazać dowolne właściwości! + +## Przekazywanie wartości do komponentu {/*passing-props-to-a-component*/} + +W poniższym kodzie komponent `Profile` nie przekazuje swojemu potomkowi `Avatar` żadnych wartości: + +```js +export default function Profile() { + return ( + + ); +} +``` + +Aby dodać do komponentu `Avatar` właściwości, wystarczą dwa kroki. + +### Krok 1: Przekaż właściwości do komponentu potomnego {/*step-1-pass-props-to-the-child-component*/} + +Najpierw przekażmy do komponentu `Avatar` jakieś wartości. Na przykład, niech będą to `person` (obiekt) oraz `size` (liczba): + +```js +export default function Profile() { + return ( + + ); +} +``` + +> Jeśli nie wiesz lub nie pamiętasz, o co chodzi z podwójnymi nawiasami klamrowymi za `person=`: [to po prostu obiekt](/learn/javascript-in-jsx-with-curly-braces#using-double-curlies-css-and-other-objects-in-jsx) zapisany wewnątrz JSX-owych klamerek. + +Teraz możemy odczytać te wartości wewnątrz komponentu `Avatar`. + +### Krok 2: Odczytaj wartości wewnątrz komponentu potomnego {/*step-2-read-props-inside-the-child-component*/} + +Aby odczytać te właściwości, wypiszmy ich nazwy oddzielone przecinkiem i zapisane wewnątrz `({` oraz `})` zaraz po słowach `function Avatar`. Dzięki temu będziemy mogli odwołać się do nich jak do zmiennych. + +```js +function Avatar({ person, size }) { + // tutaj można używać person i size +} +``` + +Teraz wystarczy dodać do komponentu `Avatar` logikę, która używa właściwości `person` i `size` do renderowania - i gotowe! + +To, co wyrenderuje `Avatar`, możemy kontrolować na wiele różnych sposobów, przekazując różne wartości dla właściwości. Spróbuj zmienić którąś z nich! + + + +```js App.js +import { getImageUrl } from './utils.js'; + +function Avatar({ person, size }) { + return ( + {person.name} + ); +} + +export default function Profile() { + return ( +
+ + + +
+ ); +} +``` + +```js utils.js +export function getImageUrl(person, size = 's') { + return ( + 'https://i.imgur.com/' + + person.imageId + + size + + '.jpg' + ); +} +``` + +```css +body { min-height: 120px; } +.avatar { margin: 10px; border-radius: 50%; } +``` + +
+ +Właściwości pozwalają myśleć o komponentach nadrzędnych i potomnych jako o bytach niezależnych. Możemy, na przykład, zmienić wartości przekazywane przez właściwości `person` i `size` w `Profile` i nie musimy wiedzieć, jak `Avatar` z nich korzysta. Podobnie możemy zmienić sposób użycia tych wartości w `Avatar` bez patrzenia na kod `Profile`. + +Możesz myśleć o właściwościach jak o "pokrętłach", którymi można sterować. Pełnią taką samą rolę co argumenty w funkcjach - tak naprawdę właściwości _są_ jedynym argumentem dla komponentu! Funkcyjne komponenty reactowe przyjmują jeden argument - obiekt `props`: + +```js +function Avatar(props) { + let person = props.person; + let size = props.size; + // ... +} +``` + +Zwykle jednak nie ma potrzeby korzystać z samego obiektu `props`, dlatego zazwyczaj się je destrukturyzuje na poszczególne właściwości. + + + +**Nie zapomnij o parze klamer `{` i `}`** wewnątrz nawiasów okrągłych `(` i `)`: + +```js +function Avatar({ person, size }) { + // ... +} +``` + +Powyższy zapis nazywamy ["destrukturyzacją"](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_a_function_parameter). Jest on równoważny do odczytu właściwości z parametru funkcji: + +```js +function Avatar(props) { + let person = props.person; + let size = props.size; + // ... +} +``` + + + +## Określanie domyślnej wartości dla właściwości {/*specifying-a-default-value-for-a-prop*/} + +Jeśli chcesz nadać właściwości domyślną wartość, która będzie użyta za każdym razem, gdy nie przekażemy żadnej wartości do komponentu, możesz to zrobić dodając do zapisu destrukturyzującego symbol `=` i podając po nim wartość domyślną: + +```js +function Avatar({ person, size = 100 }) { + // ... +} +``` + +Teraz gdy wyrenderujemy `` bez podawania właściwości `size`, zostanie ona ustawiona na wartość `100`. + +Wartość domyślna jest używana tylko wtedy, gdy właściwość `size` zostanie pominięta lub otrzyma wartość `size={undefined}`. Jeśli jednak przekażesz `size={null}` lub `size={0}`, domyślna wartość **nie** zostanie użyta. + +## Przekazywanie właściwości za pomocą operatora rozwinięcia {/*forwarding-props-with-the-jsx-spread-syntax*/} + +Niekiedy przekazywanie właściwości może okazać się bardzo uciążliwe: + +```js +function Profile({ person, size, isSepia, thickBorder }) { + return ( +
+ +
+ ); +} +``` + +Ogólnie rzecz biorąc, nie ma niczego złego w powtarzającym się kodzie - czasami może to nawet pozytywnie wpłynąć na jego czytelność. Z reguły jednak zależy nam na zwięzłości. Niektóre komponenty przekazują potomkom wszystkie swoje właściwości, jak to ma miejsce w przypadku `Profile` i `Avatar` poniżej. Z racji tego, że `Profile` nie korzysta z żadnej z właściwości, warto użyć operatora rozwinięcia (_ang._ spread operator): + +```js +function Profile(props) { + return ( +
+ +
+ ); +} +``` + +To sprawi, że wszystkie właściwości komponentu `Profile` trafią do `Avatar` bez konieczności wypisywania każdej z nich. + +**Używaj operatora rozwinięcia z umiarem.** Jeśli nagminnie używasz go w niemal każdym komponencie, to coś jest nie tak. Zwykle świadczy to o potrzebie podzielenia komponentów i przekazania potomków jako JSX. Ale o tym za chwilę! + +## Przekazywanie potomków jako JSX {/*passing-jsx-as-children*/} + +Dość często można spotkać takie oto zagnieżdżenie wbudowanych znaczników przeglądarkowych: + +```js +
+ +
+``` + +W podobny sposób można także zagnieździć własne komponenty: + +```js + + + +``` + +Kiedy zagnieżdżasz jakiś kod wewnątrz znacznika JSX, komponent nadrzędny do tego kodu otrzyma go jako wartość we właściwości `children`. Dla przykładu, poniższy komponent `Card` otrzyma właściwość `children` ustawioną na `` i wyrenderuje ją wewnątrz kontenera `div`: + + + +```js App.js +import Avatar from './Avatar.js'; + +function Card({ children }) { + return ( +
+ {children} +
+ ); +} + +export default function Profile() { + return ( + + + + ); +} +``` + +```js Avatar.js +import { getImageUrl } from './utils.js'; + +export default function Avatar({ person, size }) { + return ( + {person.name} + ); +} +``` + +```js utils.js +export function getImageUrl(person, size = 's') { + return ( + 'https://i.imgur.com/' + + person.imageId + + size + + '.jpg' + ); +} +``` + +```css +.card { + width: fit-content; + margin: 5px; + padding: 5px; + font-size: 20px; + text-align: center; + border: 1px solid #aaa; + border-radius: 20px; + background: #fff; +} +.avatar { + margin: 20px; + border-radius: 50%; +} +``` + +
+ +Spróbuj zastąpić `` wewnątrz `` jakimś tekstem, aby zobaczyć na własne oczy, że komponent `Card` może opakowywać dowolną treść. Nie musi on "wiedzieć", co renderuje. Ten wzorzec ma szerokie spektrum zastosowań i z pewnością spotkasz się z nim jeszcze nieraz. + +Komponent z właściwością `children` można sobie wyobrazić jako taki z "dziurą", którą komponent nadrzędny może "zapełnić" dowolnym kodem JSX. Dość często stosuje się `children` w komponentach opakowujących coś wizualnie: panelach, siatkach itp. Więcej na ten temat dowiesz się w rozdziale pt. [Wyodrębnianie komponentów układających interfejs](/learn/extracting-layout-components). + + + +## Jak właściwości zmieniają się w czasie {/*how-props-change-over-time*/} + +Komponent `Clock` przedstawiony poniżej otrzymuje od swojego "rodzica" dwie właściwości: `color` oraz `time`. (Celowo pominęliśmy tu kod rodzica, ponieważ korzysta on ze [stanu](/learn/state-a-components-memory), o którym będzie mowa w dalszych rozdziałach.) + +Spróbuj zmienić kolor, wybierając opcję z poniższej listy rozwijanej: + + + +```js Clock.js active +export default function Clock({ color, time }) { + return ( +

+ {time} +

+ ); +} +``` + +```js App.js hidden +import { useState, useEffect } from 'react'; +import Clock from './Clock.js'; + +function useTime() { + const [time, setTime] = useState(() => new Date()); + useEffect(() => { + const id = setInterval(() => { + setTime(new Date()); + }, 1000); + return () => clearInterval(id); + }, []); + return time; +} + +export default function App() { + const time = useTime(); + const [color, setColor] = useState('lightcoral'); + return ( +
+

+ Pick a color:{' '} + +

+ +
+ ); +} +``` + +
+ +Ten przykład pokazuje, że **komponent może otrzymywać wartości właściwości zmienne w czasie.** Właściwości nie są zawsze statyczne! Tutaj wartość dla `time` zmienia się co sekundę, a dla `color` w momencie wybrania opcji z listy rozwijanej. Właściwości odzwierciedlają dane komponentu w określonym momencie, a nie tylko na początku. + +Warto jednak pamiętać, że właściwości są [niemutowalne (_ang_. immutable)](https://en.wikipedia.org/wiki/Immutable_object) — określenie to pochodzi z informatyki i oznacza "niezmienność". Kiedy komponent chce zmienić swoje właściwości (na przykład w odpowiedzi na interakcję użytkownika lub nowe dane), musi "poprosić" swojego "rodzica", aby ten przekazał mu _inne wartości_ - czyli nowy obiekt! Wtedy stare właściwości zostaną zapomniane, a niedługo potem silnik JavaScriptu odzyska zajmowaną przez nie pamięć. + +**Nie próbuj "zmieniać właściwości".** Kiedy zechcesz zareagować na dane wprowadzone przez użytkownika (jak np. zmiana wybranego koloru), musisz "ustawić stan", o czym nauczysz się w rozdziale pt. [Stan: Pamięć komponentu](/learn/state-a-components-memory). + + + +* Aby przekazać właściwości, dodaj je do kodu JSX, tak jak to robisz z atrybutami w HTML-u. +* Aby odczytać wartości właściwości, użyj destrukturyzacji `function Avatar({ person, size })`. +* Możesz ustawić domyślną wartość, np. `size = 100`, która zostanie użyta, gdy właściwość nie ma wartości lub jest ona ustawiona na `undefined`. +* Możesz przekazać wszystkie właściwości za pomocą operatora rozwinięcia ``; ale nie nadużywaj tego sposobu! +* Zagnieżdżony kod JSX, jak np. ``, zostanie przekazany do komponentu `Card` jako właściwość `children`. +* Właściwości są jak niezmienialne "migawki" z danego momentu w czasie: każde renderowanie komponentu dostarcza nową wersję właściwości. +* Nie można zmieniać wartości właściwości. Jeśli potrzebujesz interaktywności, musisz ustawiać stan. + + + + + + + +### Wyodrębnij komponent {/*extract-a-component*/} + +Ten komponent `Gallery` zawiera bardzo podobny kod dla dwóch profili. Wyodrębnij z niego komponent `Profile`, aby zmniejszyć powtarzalność w kodzie. Następnie pomyśl, jakie właściwości należy przekazać do `Profile`. + + + +```js App.js +import { getImageUrl } from './utils.js'; + +export default function Gallery() { + return ( +
+

Wybitni naukowcy

+
+

Maria Skłodowska-Curie

+ Maria Skłodowska-Curie +
    +
  • + Profesja: + fizyka i chemia +
  • +
  • + Nagrody: 4 + (Nagroda Nobla w dziedzinie fizyki, Nagroda Nobla w dziedzinie chemii, Medal Davy'ego, Medal Matteucciego) +
  • +
  • + Odkrycia: + polon (pierwiastek) +
  • +
+
+
+

Katsuko Saruhashi

+ Katsuko Saruhashi +
    +
  • + Profesja: + geochemia +
  • +
  • + Nagrody: 2 + (Nagroda Miyake w dziedzinie geochemii, Nagroda Tanaki) +
  • +
  • + Odkrycia: + metoda pomiaru dwutlenku węgla w wodzie morskiej +
  • +
+
+
+ ); +} +``` + +```js utils.js +export function getImageUrl(imageId, size = 's') { + return ( + 'https://i.imgur.com/' + + imageId + + size + + '.jpg' + ); +} +``` + +```css +.avatar { margin: 5px; border-radius: 50%; min-height: 70px; } +.profile { + border: 1px solid #aaa; + border-radius: 6px; + margin-top: 20px; + padding: 10px; +} +h1, h2 { margin: 5px; } +h1 { margin-bottom: 10px; } +ul { padding: 0px 10px 0px 20px; } +li { margin: 5px; } +``` + +
+ + + +Zacznij od wyodrębnienia kodu dla jednej z naukowczyń. Następnie znajdź fragmenty, które nie pokrywają się z nim w drugim przykładzie, i przerób kod tak, aby dało się nimi sterować za pomocą właściwości. + + + + + +W tym rozwiązaniu komponent `Profile` przyjmuje kilka właściwości: `imageId` (tekst), `name` (tekst), `profession` (tekst), `awards` (tablica napisów), `discovery` (tekst) oraz `imageSize` (liczba). + +Zwróć uwagę, że właściwość `imageSize` ma wartość domyślną. To dlatego nie podajemy jej w `Gallery`. + + + +```js App.js +import { getImageUrl } from './utils.js'; + +function Profile({ + imageId, + name, + profession, + awards, + discovery, + imageSize = 70 +}) { + return ( +
+

{name}

+ {name} +
    +
  • Profesja: {profession}
  • +
  • + Nagrody: {awards.length} + ({awards.join(', ')}) +
  • +
  • + Odkrycia: + {discovery} +
  • +
+
+ ); +} + +export default function Gallery() { + return ( +
+

Wybitni naukowcy

+ + +
+ ); +} +``` + +```js utils.js +export function getImageUrl(imageId, size = 's') { + return ( + 'https://i.imgur.com/' + + imageId + + size + + '.jpg' + ); +} +``` + +```css +.avatar { margin: 5px; border-radius: 50%; min-height: 70px; } +.profile { + border: 1px solid #aaa; + border-radius: 6px; + margin-top: 20px; + padding: 10px; +} +h1, h2 { margin: 5px; } +h1 { margin-bottom: 10px; } +ul { padding: 0px 10px 0px 20px; } +li { margin: 5px; } +``` + +
+ +Zwróć uwagę, że nie potrzebujesz mieć osobnej właściwości `awardCount`, jeśli `awards` jest tablicą. Aby poznać liczbę nagród, możesz przecież odczytać wartość `awards.length`. Pamiętaj, że właściwości mogą przyjmować dowolne wartości, nawet tablice! + +Inne rozwiązanie, nieco bardziej podobne do poprzednich przykładów na tej stronie, polega na zgrupowaniu wszystkich informacji w jeden obiekt, a następnie przekazaniu go jako jedna właściwość do komponentu: + + + +```js App.js +import { getImageUrl } from './utils.js'; + +function Profile({ person, imageSize = 70 }) { + const imageSrc = getImageUrl(person) + + return ( +
+

{person.name}

+ {person.name} +
    +
  • + Profesja: {person.profession} +
  • +
  • + Nagrody: {person.awards.length} + ({person.awards.join(', ')}) +
  • +
  • + Odkrycia: + {person.discovery} +
  • +
+
+ ) +} + +export default function Gallery() { + return ( +
+

Wybitni naukowcy

+ + +
+ ); +} +``` + +```js utils.js +export function getImageUrl(person, size = 's') { + return ( + 'https://i.imgur.com/' + + person.imageId + + size + + '.jpg' + ); +} +``` + +```css +.avatar { margin: 5px; border-radius: 50%; min-height: 70px; } +.profile { + border: 1px solid #aaa; + border-radius: 6px; + margin-top: 20px; + padding: 10px; +} +h1, h2 { margin: 5px; } +h1 { margin-bottom: 10px; } +ul { padding: 0px 10px 0px 20px; } +li { margin: 5px; } +``` + +
+ +Mimo że składnia tutaj wygląda nieco inaczej, ponieważ opisujemy właściwości obiektu javascriptowego, a nie serię atrybutów JSX-owych, obydwa przykłady są równoważne i możesz używać tego, który ci się bardziej podoba. + +
+ +### Dostosuj rozmiar obrazka na podstawie właściwości {/*adjust-the-image-size-based-on-a-prop*/} + +W kodzie poniżej `Avatar` otrzymuje właściwość numeryczną `size`, która określa szerokość i wysokość dla ``. Właściwość `size` jest w tym przykładzie ustawiona na `40`. Jednakże, jeśli otworzysz obrazek w nowej karcie, zobaczysz, że jest on w rzeczywistości większy (`160` pikseli). Prawdziwy rozmiar obrazka jest określany na podstawie rozmiaru miniatury, o jaką prosisz. + +Zmień komponent `Avatar` tak, aby ustawiał rozmiar obrazka na podstawie właściwości `size`. Konkretniej, jeśli `size` jest mniejszy niż `90`, przekaż wartość `'s'` ("small") zamiast `'b'` ("big") do funkcji `getImageUrl`. Sprawdź, czy twoje zmiany działają, renderując awatary z różnymi wartościami `size` i otwierając obrazki w nowej zakładce. + + + +```js App.js +import { getImageUrl } from './utils.js'; + +function Avatar({ person, size }) { + return ( + {person.name} + ); +} + +export default function Profile() { + return ( + + ); +} +``` + +```js utils.js +export function getImageUrl(person, size) { + return ( + 'https://i.imgur.com/' + + person.imageId + + size + + '.jpg' + ); +} +``` + +```css +.avatar { margin: 20px; border-radius: 50%; } +``` + + + + + +Oto możliwe rozwiązanie: + + + +```js App.js +import { getImageUrl } from './utils.js'; + +function Avatar({ person, size }) { + let thumbnailSize = 's'; + if (size > 90) { + thumbnailSize = 'b'; + } + return ( + {person.name} + ); +} + +export default function Profile() { + return ( + <> + + + + ); +} +``` + +```js utils.js +export function getImageUrl(person, size) { + return ( + 'https://i.imgur.com/' + + person.imageId + + size + + '.jpg' + ); +} +``` + +```css +.avatar { margin: 20px; border-radius: 50%; } +``` + + + +Możesz także pokazać nieco ostrzejszą wersję obrazka dla ekranów z wysokim DPI, biorąc pod uwagę wartość [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio): + + + +```js App.js +import { getImageUrl } from './utils.js'; + +const ratio = window.devicePixelRatio; + +function Avatar({ person, size }) { + let thumbnailSize = 's'; + if (size * ratio > 90) { + thumbnailSize = 'b'; + } + return ( + {person.name} + ); +} + +export default function Profile() { + return ( + <> + + + + + ); +} +``` + +```js utils.js +export function getImageUrl(person, size) { + return ( + 'https://i.imgur.com/' + + person.imageId + + size + + '.jpg' + ); +} +``` + +```css +.avatar { margin: 20px; border-radius: 50%; } +``` + + + +Właściwości pozwalają zamknąć logikę tego typu wewnątrz komponentu `Avatar` (i zmienić ją w razie potrzeb później), tak aby inni, korzystając z niego, nie musieli zastanawiać się, jak te obrazki są pobierane i jak dobierany jest ich rozmiar. + + + +### Przekazywanie kodu JSX do właściwości `children` {/*passing-jsx-in-a-children-prop*/} + +Wyodrębnij z poniższego kodu komponent `Card`, a następnie użyj właściwości `children` tak, by przekazać do niego inny kod JSX-owy: + + + +```js +export default function Profile() { + return ( +
+
+
+

Zdjęcie

+ Aklilu Lemma +
+
+
+
+

O postaci

+

Aklilu Lemma był wybitnym etiopskim naukowcem, który wynalazł naturalny sposób leczenia schistosomatozy.

+
+
+
+ ); +} +``` + +```css +.card { + width: fit-content; + margin: 20px; + padding: 20px; + border: 1px solid #aaa; + border-radius: 20px; + background: #fff; +} +.card-content { + text-align: center; +} +.avatar { + margin: 10px; + border-radius: 50%; +} +h1 { + margin: 5px; + padding: 0; + font-size: 24px; +} +``` + +
+ + + +Dowolny kod JSX, który napiszesz wewnątrz znacznika, zostanie przekazany do komponentu jako właściwość `children`. + + + + + +Oto w jaki sposób można użyć komponentu `Card` w obu miejscach: + + + +```js +function Card({ children }) { + return ( +
+
+ {children} +
+
+ ); +} + +export default function Profile() { + return ( +
+ +

Zdjęcie

+ Aklilu Lemma +
+ +

O postaci

+

Aklilu Lemma był wybitnym etiopskim naukowcem, który wynalazł naturalny sposób leczenia schistosomatozy.

+
+
+ ); +} +``` + +```css +.card { + width: fit-content; + margin: 20px; + padding: 20px; + border: 1px solid #aaa; + border-radius: 20px; + background: #fff; +} +.card-content { + text-align: center; +} +.avatar { + margin: 10px; + border-radius: 50%; +} +h1 { + margin: 5px; + padding: 0; + font-size: 24px; +} +``` + +
+ +Możesz także stworzyć oddzielną właściwość `title`, jeśli chcesz, aby `Card` zawsze miał tytuł: + + + +```js +function Card({ children, title }) { + return ( +
+
+

{title}

+ {children} +
+
+ ); +} + +export default function Profile() { + return ( +
+ + Aklilu Lemma + + +

Aklilu Lemma był wybitnym etiopskim naukowcem, który wynalazł naturalny sposób leczenia schistosomatozy.

+
+
+ ); +} +``` + +```css +.card { + width: fit-content; + margin: 20px; + padding: 20px; + border: 1px solid #aaa; + border-radius: 20px; + background: #fff; +} +.card-content { + text-align: center; +} +.avatar { + margin: 10px; + border-radius: 50%; +} +h1 { + margin: 5px; + padding: 0; + font-size: 24px; +} +``` + +
+ +
+
\ No newline at end of file diff --git a/beta/src/pages/learn/state-a-components-memory.md b/beta/src/pages/learn/state-a-components-memory.md index 2b9dd40db..9ed593ac8 100644 --- a/beta/src/pages/learn/state-a-components-memory.md +++ b/beta/src/pages/learn/state-a-components-memory.md @@ -1,5 +1,5 @@ --- -title: "State: A Component's Memory" +title: "Stan: Pamięć komponentu" --- diff --git a/beta/src/sidebarLearn.json b/beta/src/sidebarLearn.json index 333455de6..2d10ba5e7 100644 --- a/beta/src/sidebarLearn.json +++ b/beta/src/sidebarLearn.json @@ -59,7 +59,7 @@ "path": "/learn/javascript-in-jsx-with-curly-braces" }, { - "title": "Passing Props to a Component", + "title": "Przekazywanie wartości do komponentu", "path": "/learn/passing-props-to-a-component" }, { @@ -86,7 +86,7 @@ "path": "/learn/responding-to-events" }, { - "title": "State: A Component's Memory", + "title": "Stan: Pamięć komponentu", "path": "/learn/state-a-components-memory" }, {