javascript React:更新子组件而不重新渲染父组件

zaqlnxep  于 2023-08-02  发布在  Java
关注(0)|答案(4)|浏览(258)

下面是一个简单的例子:

const { Component } = React
const { render } = ReactDOM

const Label = ({ text }) => (
  <p>{text}</p>
)

const Clock = ({ date }) => (
  <div>{date.toLocaleTimeString()}</div>
)

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      date: new Date()
    }
  }
  
  componentDidMount() {
    this.interval = setInterval(
      () => this.setState({ date: new Date() }),
      1000
    )
  }
  
  componentWillUnmount() {
    clearInterval(this.interval)
  }
  
  updateTime() {
    
  }
  
  render() {
    return (
      <div>
        <Label text="The current time is:" />
        <Clock date={this.state.date} />
      </div>
    )
  }
  
}

render(<App />, document.getElementById('app'))

个字符
this.setState({ date: new Date() })每秒被调用一次,用当前时间更新时钟。据我所知,setState调用App上的render方法,导致整个组件重新渲染,包括Label。
有没有一种方法可以将日期传递给时钟(导致它被重新渲染),而不需要重新渲染整个应用程序组件?这对性能有多大影响?

2ledvvac

2ledvvac1#

如果要更新子项而不更新父项,则状态必须在子项中。你可以将状态getter / setter从子对象传递给父对象,以便能够读取和更新它:

function Child({onMount}) {
  const [value, setValue] = useState(0);

  useEffect(() => {
    onMount([value, setValue]);
  }, [onMount, value]);

  return (
    <div>
      {value}
    </div>    
  );
};

function Parent() {

  let value = null;
  let setValue = null;
  
  const onChildMount = (dataFromChild) => {
    value = dataFromChild[0];
    setValue = dataFromChild[1];
  };

  // Call setValue to update child without updating parent

  return (
    <div>
      <Child onMount={onChildMount}/>
    </div>    
  );
};

字符串
由于const [value, setValue] = useState(0);位于Child中,因此在更新值时,只有子组件将重新呈现。此外,由于ParentonChildMount接收valuesetValue,因此父节点可以使用它们来更新子节点,而无需重新呈现父节点。

vzgqcmou

vzgqcmou2#

你想要的不可能。要将prop传递给子组件,父组件的状态或prop应该以某种方式更改。如你所知,这显然会触发一个重新渲染,所以所有的子元素都会重新渲染。要进行更新,您的Clock组件应该在这种情况下重新呈现和卸载/重新装载,以反映DOM更改。
如果你的应用程序不是很大,也没有那么多的子程序,那么就不要纠结这个问题,因为渲染并不那么昂贵。代价最高的是组件的DOM操作。在这里,React区分了真实的和虚拟DOM,并且即使重新渲染也不会卸载/重新装载Label组件。但是,如果您将Label组件编写为PureComponent,则不会重新呈现。但是对于像这样更新Clock组件,没有办法。

class Label extends React.PureComponent {
  render() {
    console.log("rendered");
    return (<p>{this.props.text}</p>)
  }
}

const Clock = ({ date }) => (
  <div>{date.toLocaleTimeString()}</div>
)

class App extends React.Component {

  constructor() {
    super()
    this.state = {
      date: new Date()
    }
  }

  componentWillMount() {
    this.interval = setInterval(
      () => this.setState({ date: new Date() }),
      1000
    )
  }

  componentWillUnmount() {
    clearInterval(this.interval)
  }

  updateTime() {

  }

  render() {
    return (
      <div>
        <Label text="The current time is:" />
        <Clock date={this.state.date} />
      </div>
    )
  }

}

字符串

p5cysglq

p5cysglq3#

使用子组件的参考,可以实现:

export const parentA = () => {
  let ref = useRef<View>(null);
  const updateChild () {
    ref.current.props.setNativeProps({style:{backgroundColor: 'gray',}})
  }
  return (
    <childA>
      <Text>Test</Text>
    </childA>
  );
};

export const childA = () => {
  return <View style={{backgroundColor: 'green',}}></View>;
};

字符串

g2ieeal7

g2ieeal74#

使用useImperativeHandleReact.forwardRef,您可以将状态从Child返回到Parent作为ref:

function Parent({ children }) {
  const childRef = useRef(null);
  
  function printValue() {
    console.log(childRef.current.value);
  }

  function setValueToOne() {
    childRef.current.setValue(1);
  }

  return (
    <div>
      <Child ref={childRef} style={{ color: "red" }} />
    </div>
  );
}

const Child = forwardRef(function Child({ style }, ref), () => {
  const [value, setValue] = useState(null);

  useImperativeHandle(ref, () => {
    return {
      value,
      setValue,
    }   
  }, []);

  return <div style={style}>{value}</div>
})

字符串

相关问题